美文网首页
JavaScript 中的 this

JavaScript 中的 this

作者: mtry | 来源:发表于2020-08-21 16:11 被阅读0次

    主要内容

    • this 主要解决的问题
    • 不同上下文中的 this
    • call 和 apply
    • bind

    this 主要解决的问题

    JavaScript 允许在函数体内部,引用当前环境的其他变量。

    var f = function () {
      console.log(x);
    };
    

    上面代码中,函数体里面使用了变量x。该变量由运行环境提供。

    现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

    var f = function () {
      console.log(this.x);
    }
    

    上面代码中,函数体里面的this.x就是指当前运行环境的x。

    var f = function () {
      console.log(this.x);
    }
    
    var x = 1;
    var obj = {
      f: f,
      x: 2,
    };
    
    // 单独执行
    f() // 1
    
    // obj 环境执行
    obj.f() // 2
    

    不同上下文中的 this

    1、全局上下文

    无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

    2、函数上下文

    在函数内部,this 的值取决于函数被调用的方式。

    非严格模式

    function f1(){
      return this;
    }
    //在浏览器中:
    f1() === window;   //在浏览器中,全局对象是window
    
    //在Node中:
    f1() === globalThis;  
    

    严格模式

    function f2(){
      "use strict"; // 这里是严格模式
      return this;
    }
    
    f2() === undefined; // true
    

    注意:箭头函数 this 永远绑定了定义箭头函数所在的那个对象

    // 创建一个含有bar方法的obj对象,
    // bar返回一个函数,
    // 这个函数返回this,
    // 这个返回的函数是以箭头函数创建的,
    // 所以它的this被永久绑定到了它外层函数的this。
    // bar的值可以在调用中设置,这反过来又设置了返回函数的值。
    var obj = {
      bar: function() {
        var x = (() => this);
        return x;
      }
    };
    
    // 作为obj对象的一个方法来调用bar,把它的this绑定到obj。
    // 将返回的函数的引用赋值给fn。
    var fn = obj.bar();
    
    // 直接调用fn而不设置this,
    // 通常(即不使用箭头函数的情况)默认为全局对象
    // 若在严格模式则为undefined
    console.log(fn() === obj); // true
    
    // 但是注意,如果你只是引用obj的方法,
    // 而没有调用它
    var fn2 = obj.bar;
    // 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。
    console.log(fn2()() == window); // true
    

    3、类上下文

    this 在类中的表现与在函数中类似,因为类本质上也是函数,但也有一些区别和注意事项。在类的构造函数中,this 是一个常规对象,类中所有非静态的方法都会被添加到 this 的原型中;派生类的构造函数没有初始的 this 绑定,使用前需要先调用 super。

    class Example {
      constructor() {
        const proto = Object.getPrototypeOf(this);
        console.log(Object.getOwnPropertyNames(proto));
      }
      first(){}
      second(){}
      static third(){}
    }
    
    new Example(); // ['constructor', 'first', 'second']
    

    注意:如果你使用了 ES6 的 class 语法,所有在 class 中声明的方法都会自动地使用严格模式

    call 和 apply

    在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

    function add(c, d) {
      return this.a + this.b + c + d;
    }
    
    var o = {a: 1, b: 3};
    
    // 第一个参数是用作“this”的对象
    // 其余参数用作函数的参数
    add.call(o, 5, 7); // 16
    
    // 第一个参数是用作“this”的对象
    // 第二个参数是一个数组,数组中的两个成员用作函数参数
    add.apply(o, [10, 20]); // 34
    

    apply 和 call 的区别

    对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。

    var func = function(arg1, arg2) {
         
    };
    func.call(this, arg1, arg2);
    func.apply(this, [arg1, arg2])
    

    bind

    ECMAScript 5 引入了 Function.prototype.bind()。调用 f.bind(someObject) 会创建一个与 f 具有相同函数体和作用域的函数,但是在这个新函数中,this 将永久地被绑定到了 bind 的第一个参数,无论这个函数是如何被调用的。

    function f(){
      return this.a;
    }
    
    var g = f.bind({a:"azerty"});
    console.log(g()); // azerty
    
    var h = g.bind({a:'yoo'}); // bind只生效一次!
    console.log(h()); // azerty
    
    var o = {a:37, f:f, g:g, h:h};
    console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty
    

    bind 与 apply、call 的区别

    当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

    小结

    • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
    • apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
    • apply 、 call 、bind 三者都可以利用后续参数传参;
    • bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。

    参考资料

    相关文章

      网友评论

          本文标题:JavaScript 中的 this

          本文链接:https://www.haomeiwen.com/subject/slaujktx.html