# 关于 this

当 JavaScript 执行到一段可执行代码时,会创建一个可执行上下文。执行上下文可以理解为当前代码的执行环境。 执行上下文的周期可以分为两个阶段。

  1. 创建阶段

    在这个阶段,可执行上下文会创建变量对象、建立作用域链以及确定 this 指向问题

  2. 代码执行阶段

    创建完成后,就开始执行代码,完成变量赋值、函数引用以及执行其他代码。

# 如何判断 this 的指向

this 对象是基于当前运行环境执行时所绑定的。

  1. 隐式绑定

    通过某个对象的属性指向函数,再通过这个对象属性调用函数。这就是函数在调用的时候给绑定了上下文对象,或者说被这个对象拥有。

    function getName() {
      console.log('this:%s,name:%s', this, this.name);
    }
    var name = 'globalName';
    var bar = {
      name: 'barName',
      getName: getName,
    };
    bar.getName();
    

    getName是一个单独函数,严格来说它不属于 bar,只不过在调用的时候使用bar.getName引用函数,也就是说 getName 函数被调用的时候添加了对 bar 的引用,当函数有引用文对象的时候,函数内部的 this 就会被绑定到 bar。

    所以上述代码执行的结果如下图所示: '运行结果'

    现在代码稍微修改一下










     


    var name = 'globalName';
    function getName() {
      console.log('this:%s,name:%s', this, this.name);
    }
    var bar = {
      name: 'barName',
      getName: getName,
    };
    //将引用地址赋值给一个新的变量
    const fn = bar.getName;
    fn();
    

    运行结果如下: '运行结果'

    函数getName丢失了绑定对象,变成了 全局对象(或者 undefined,这取决于是否函数是否在严格模式下执行)。

  2. 显示绑定

    通过 call 或者 apply 方法强制把 this 绑定到指定的对象。








     
     
     
     

    var name = 'globalName';
    function getName(...params) {
      console.log('this:%s,name:%s,params:%s', this, this.name, params);
    }
    var bar = {
      name: 'barName',
    };
    getName.apply(bar, [10, 11]);
    getName.call(bar, 10, 12);
    const fn = getName.bind(bar, 10, 13);
    fn();
    

    温馨提示

    applycall区别仅是其他参数传递方式不同。applycall第一个参数如果传递的值为基本类型,JavaScript 会有一个装箱的操作,也就是把基本类型包装成它对应的对象形式。

    为了解决隐式绑定出现 this 丢失的问题,我们可以增加一个中间函数解决这个问题。

    var name = 'globalName';
    function getName() {
      console.log('this:%s,name:%s', this, this.name);
    }
    var bar = {
      name: 'barName',
    };
    function foo() {
      //强制将getName函数的this绑定到bar
      return getName.apply(bar);
    }
    foo();
    
  3. new 绑定

    使用 new 的方式创建的实例,this 指向这个实例(新创建的对象)。 使用 new 操作符创建的实例,经历 4 个步骤。

    1. 创建一个新对象
    2. 新对象的 proto 属性指向函数的原型
    3. 将构造函数的作用域赋给新对象(所以 this 是指向这个新对象)。执行构造函数中的代码,为这个新对象添加属性
    4. 如果函数没有返回其他对象,则返回这个新对象
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    // 在这里可以把Person当作一个构造函数
    // 所以在new Person的时候就相当于把它的作用域指向了p
    // 所以Person中的this是指向p的
    const p = new Person();
    
  4. 默认绑定

    this 不能使用其他三个规则的则使用默认绑定。默认绑定就是函数独立运行所在的位置,通常都是指全局对象或者 undefined,这取决函数是否在严格模式下执行。

# 关于箭头函数的 this

箭头函数本身是没有 this 这个概念的,内部的 this 取决于包裹箭头函数的第一个普通函数 this。

# 总结

  1. 是否是使用 new 的方式生成?如果是则 this 绑定到该对象
  2. 是否使用 call、apply 以及 bind 等显示绑定方式?
  3. 是否使用隐式绑定?则 this 绑定到引用上下文对象
  4. 都不是则使用默认规则

箭头函数是根据当前此法作用域来决定 this,具体来说就是继承外层函数绑定的 this。