你不知道的JavaScript阅读笔记-this篇

119 阅读2分钟

每个函数的 this 是在调用 时被绑定的,完全取决于函数的调用位置

默认绑定

独立函数调用

function foo() { 
    console.log( this.a ); 
}
var a = 2;
foo(); // 2

foo() 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用 默认绑定,无法应用其他规则

function foo() { 
    "use strict"; 
    console.log( this.a );
}
var a = 2; 
foo(); // TypeError: this is undefined

虽然 this 的绑定规则完全取决于调用位置,但是只 有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo() 的调用位置无关

隐式绑定

调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

  function foo() { 
      console.log( this.a ); 
  }
  var obj = {
      a: 2,
      foo: foo 
  };
  obj.foo(); // 2
  • 对象属性引用链中只有最顶层或者说最后一层会影响调用位置
  function foo() { 
      console.log( this.a ); 
  }
  var obj2 = {
      a: 42,
      foo: foo
  };
  var obj1 = {
      a: 2,
      obj2: obj2 
  };
  obj1.obj2.foo(); // 42
  • 隐式丢失
function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo 
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性 bar(); // "oops, global"
function foo() {
  console.log( this.a );
}
function doFoo(fn) { 
  // fn 其实引用的是 foo 
  fn(); // <-- 调用位置! 
}
var obj = { 
  a: 2, 
  foo: foo 
};
var a = "oops, global"; // a 是全局对象的属性 doFoo( obj.foo ); // "oops, global"

显式绑定

bind call apply

从this 绑定的角度来说,call(..) 和 apply(..) 是一样的,它们的区别体现 在其他的参数上。

  • 硬绑定

    显式的强制绑定

    function foo(something) { 
      console.log( this.a, something );
      return this.a + something;
    }
    var obj = {
      a:2
    };
    var bar = function() {
      return foo.apply( obj, arguments );
    };
    var b = bar( 3 ); // 2 3
    
  • API调用的“上下文”
    function foo(el) { 
      console.log( el, this.id );
    }
    // 调用 foo(..) 时把 this 绑定到 obj
    var obj = { id: "awesome" };
    [1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome
    

new绑定

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行 [[ 原型 ]] 连接。
  3. 这个新对象会绑定到函数调用的 this。
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
  this.a = a; 
}
var bar = new foo(2); 
console.log( bar.a ); // 2