前言
在javascript的开发中,经常会遇到this这个关键字。简单的说 this 是个特殊变量。但是在javascript中,this 的使用经常让我感到困惑,这篇文章是阅读《你不知道的 JavaScript》中第二章 this 的全面解析笔记。
调用位置
this的绑定结果就是 this 在代码中调用的位置。注意是调用位置,而不是声明位置。如下面的代码:
var value = "global";
var obj = {
value: "obj",
foo: function () {
return this.value;
},
};
var bar = obj.foo;
console.log(bar());
console.log(obj.foo());
在这个例子中,函数 foo 是在 obj 对象的上下文中声明的,并且返回 this.value。 this 在这个函数中应该引用其声明位置的上下文,即 obj;然而 this 实际上引用了调用位置的上下文。我们可以通过比较 bar() 和 obj.foo() 的结果来观察这一点。
当我们只是简单地调用 bar() 时,this 被设置为全局对象(在浏览器中为 window),因此 this.value 返回全局的 value 变量,即 'global'。
然而,当我们调用 obj.foo() 时,this 被设置为 foo 函数被调用时的对象,即 obj。因此 this.value 返回了 obj 的 value 属性,即 'obj'。
绑定规则
this的绑定规则为 4 种,分别如下:
- 默认绑定:这是最常见的绑定,当一个函数独立调用(不是作为对象的方法,作为回调等)时,
this指向全局对象(在浏览器中为window),但是如果在严格模式下,this会被绑定到 'undefined'。
function show() {
console.log(this.a);
}
var a = "Global";
show(); // 输出: 'Global'; 在非严格模式下
- 隐式绑定:当一个函数作为对象的方法调用时,
this会被绑定到这个对象。这里注意的就是对象属性链只有最顶层或者说最后一层会影响调用位置。
var obj = {
a: "Object",
show: function () {
console.log(this.a);
},
};
obj.show(); // 输出: 'Object'
- 显式绑定:可以使用
call,apply,bind这些函数来显示的绑定this。
function show() {
console.log(this.a);
}
var obj = {
a: "Explicit",
};
show.call(obj); // 输出: 'Explicit',使用 call 函数显式绑定
new绑定:当一个函数被new关键字作为构造函数调用时,函数内部的this会被绑定到新创建的对象。
function Show() {
this.a = "New";
}
var newObj = new Show();
console.log(newObj.a); // 输出:'New'
优先级
- 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。 var bar = new foo()
- 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是 指定的对象。 var bar = foo.call(obj2)
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。 var bar = obj1.foo()
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到 全局对象。 var bar = foo()
规则总有例外,这里也一样。在某些场景下 this 的绑定行为会出乎意料,你认为应当应用其他绑定规则时,实际上应用的可能是默认绑定规则
箭头函数
箭头函数是一种特殊的函数类型,不是使用 function关键字定义的,也不使用 this 的四种规则标准。而是根据外层(函数或者全局)作用域决定的。箭头函数的绑定也无法修改(new 也不行)。他的目的是取代 this 规则。这其实和 ES6 之前代码中的 self = this机制一样。