• JS中this指向的更改


    JS中this指向的更改

    JavaScript 中 this 的指向问题 前面已经总结过,但在实际开中, 很多场景都需要改变 this 的指向。 现在我们讨论更改 this 指向的问题。

    call更改this指向

    call 的使用语法:func.call(thisArg, arg1, arg2, ...)

    call 方法需要一个指定的 this 值( this要指向的对象 )和一个或者多个参数。提供的 this 值会更改调用函数内部的 this 指向。

    // 使用 call 方法改变调用函数执行上下文的 this 指向
    var animal = '小猫';
    var times = '15小时';
    function greet() {
        let str = this.animal + '睡觉时间一般为:' + this.times;
        console.log(str);
    }
    var dogObj = {
        animal: '小狗',
        times: '8小时'
    };
    var pigObj = {
        animal: '小猪',
        times: '13小时'
    }
    greet(); // 小猫睡觉时间一般为:15小时
    greet.call(dogObj); // 小狗睡觉时间一般为:8小时
    greet.call(pigObj); // 小猪睡觉时间一般为:13小时
    greet.call(); // 小猫睡觉时间一般为:15小时
    

    当直接调用函数 greet 时,函数 greet 内部的 this 指向的是全局对象 Window。

    函数 greet 调用 call() 方法并传递对象 dogObj 时,函数 greet 内部的 this 就指向了对象 dogObj 。

    函数 greet 调用 call() 方法并传递对象 pigObj 时,函数 greet 内部的 this 就指向了对象 pigObj 。

    call()不传参的话,在严格模式下,this 的值将会是 undefined;否则将会指向全局对象 Window。

    匿名函数调用call方法:

    var books = [{
        name: 'CSS选择器',
        price: 23
    }, {
        name: 'CSS世界',
        price: 35
    }, {
        name: 'JavaScript语言设计',
        price: 55
    }];
    for (var i = 0; i < books.length; i++) {
        (function (i) {
            // 这里this指向的是call绑定的数组的每一个元素对象
            this.printf = function () {
                console.log(`${i} ${this.name}: ¥${this.price}`);
            }
            this.printf();
        }).call(books[i], i);
    }
    // 打印结果如下:
    // 0 CSS选择器: ¥23
    // 1 CSS世界: ¥35
    // 2 JavaScript语言设计: ¥55
    

    call实现继承:

    // 实现两个数相加的构造函数
    function CalcA(){
        this.add = function(a, b){
            return a + b;
        }
    }
    // 实现两个数相减的构造函数
    function CalcS(){
        this.sub = function(a, b){
            return a - b;
        }
    }
    // 计算构造函数
    function Calc(){
        console.log(this); // Calc {}
        CalcA.call(this);
        CalcS.call(this);
        console.log(this); // Calc {add: ƒ, sub: ƒ}
    }
    var calc = new Calc();
    console.log(calc.add(2, 3)); // 5
    console.log(calc.sub(10, 1));// 9
    

    构造函数 Calc 通过 call 方法使构造函数 CalcA、CalcS中的 this 指向了 Calc 自己,从而继承了它们的属性及方法。所以,构造函数 Calc 生成的实例对象也能够访问构造函数 CalcA、CalcS中的属性及方法。

    apply方法更改this指向

    apply 的使用语法:func.apply(thisArg, [argsArray])

    apply 的用法与 call 方法类似,只不过 call 方法接受的是参数列表,而 apply 方法接受的是一个数组或者类数组对象。上面的例子完全可以将 call 更换为 apply,只不过 apply 方法只能接受两个参数,而且第二个参数是一个数组或者类数组对象。

    bind方法更改this指向

    bind 的使用语法:func.bind(thisArg, arg1, arg2, ...)

    bind 的参数与 call 相同,但是 bind 返回的是一个改变this指向后的函数实例。

    var petalNum = 100;
    function Flower() {
        this.petalNum = Math.ceil(Math.random() * 10) + 1;
    }
    Flower.prototype.declare = function() {
        console.log(this);
        console.log('this is a beautiful flower with ' + this.petalNum + ' petals');
    }
    Flower.prototype.bloom = function() {
        console.log(this); // Flower {petalNum: 7}
        // 如果不绑定 this 就会指向 Window 全局对象
        window.setTimeout(this.declare, 1000);
        // bind 绑定 this,指向 Flower 的原型对象
        window.setTimeout(this.declare.bind(this), 2000);
    }
    var flower = new Flower();
    flower.bloom();
    

    实例对象 flower 调用 bloom 方法后,bloom 内的 this 指向构造函数的原型对象。

    1 秒后延迟函数调用构造函数的 declare 方法, 此时执行函数 declare 中的 this 指向 Window 。打印的结果如下:

    // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
    // this is a beautiful flower with 100 petals
    

    2 秒后延迟函数调用构造函数的 declare 方法,此时执行函数 declare 通过 bind 将 this(构造函数的原型对象)绑定。打印的结果如下:

    // 注意,此时petalNum的值时随机取的。
    // Flower {petalNum: 7}
    // this is a beautiful flower with 7 petals
    

    这里将 bind换 成 call,apply 会导致立即执行,延迟效果会失效。

    ES6的箭头函数更改this指向

    箭头函数中的 this 是在定义函数的时候绑定,而不是在执行函数的时候绑定。 所谓定义时候绑定,就是指 this 是继承自父执行上下文的 this

    var a = 1;
    var obj = {
        a: 2,
        f1: function(){
            console.log(this.a)
        },
        f2: () => {
            console.log(this.a)
        }
    }
    obj.f1(); // 2
    obj.f2(); // 1
    

    obj.f1() 执行后打印的是 2,这里好理解,obj 调用 f1 函数,那么函数中的 this 就指向调用对象 obj。可以看出,这里 this 是在执行函数的时候绑定的。

    obj.f2() 执行后打印的是 1。f2 是箭头函数,那么函数中的 this 是继承自父执行上下文的 this。这里箭头函数的父级是对象 obj,obj 的执行上下文就是全局对象 Window,那么箭头函数中的 this 就指向了全局对象了。

    再看一个例子:

    var a = 11;
    function test() {
        this.a = 22;
        let b = () => { console.log(this.a) }
        b();
    }
    test(); // 22 
    

    按着定义的理解,应该打印出 11 才对呀,因为箭头函数父级的执行上下文就是 Window 全局对象,此时打印的是全局对象的 a。

    先不要着急,先慢慢分析,上面的分析是对的,箭头函数的 this 就是指向 Window 对象。test 函数在全局环境下调用时其内部的 this 就指向了全局 Window 对象,代码中的 this.a = 22;就将全局中的 a 重新赋值了,所以箭头函数在全局对象中找到的 a 值就是 22。我们可以在控制台上输入 window.a 查看全局对象中的 a 值,结果打印 22,所以我们就不难理解箭头函数中打印的结果为什么是 22 了。如果将代码中的 this.a = 22; 修改为 var a = 22;,那么箭头函数中打印的结果就是 11 了。

    箭头函数会继承外层函数调用的 this 绑定,这和 var self = this;的绑定机制一样。箭头函数中,this 指向固定化,箭头函数根本就没有自己的 this, 所以也就不能用作构造函数使用了。

  • 相关阅读:
    4月4日 python学习总结 os pickle logging
    4月3日 python学习总结
    4月2日 python学习总结
    【Vue】vue递归组件实现多级列表
    用原生html与js写一个dialog
    【Vue】filters过滤器中不能使用this的解决方案
    【Vue】Vue中data重置问题
    【Vue】Vue渲染模板时怎么保留模板中的HTML注释
    【Vue】在 Vue 中使用 JSX
    【Vue】在.vue文件中style是必须的吗?那script是必须的吗?为什么?——函数式组件
  • 原文地址:https://www.cnblogs.com/laozhenHome/p/13287866.html
Copyright © 2020-2023  润新知