[译]浅显易懂的 this 取值规则
翻译自文章The Simple Rules to ‘this’ in Javascript。
确定什么是 this 并非难事。总的来说,通过查找函数被调用时的位置(和方法)就可以决定。遵循以下规则,按优先级排列。
规则
-
通过
new关键字调用构造函数,函数内的this是一个全新的对象。function ConstructorExample() {
console.log(this);
this.value = 10;
console.log(this);
}
new ConstructorExample();
// -> {}
// -> { value: 10 } -
通过
apply、call或bind调用一个函数,函数内的this就是传入的参数。function fn() {
console.log(this);
}
var obj = {
value: 5
};
var boundFn = fn.bind(obj);
boundFn(); // -> { value: 5 }
fn.call(obj); // -> { value: 5 }
fn.apply(obj); // -> { value: 5 } -
如果一个函数作为对象的方法调用,即使用
.符号调用该函数,this是调用该函数的对象。换句话说,当.处于被调用函数的左边,则this就是左边的对象。var obj = {
value: 5,
printThis: function() {
console.log(this);
}
};
obj.printThis(); // -> { value: 5, printThis: ƒ } -
如果函数作为普通函数调用,意味着调用方式不符合以上任意一种,
this就是全局对象。在浏览器中就是window。function fn() {
console.log(this);
}
// If called in browser:
fn(); // -> Window {stop: ƒ, open: ƒ, alert: ƒ, ...}*这个规则可以类比于规则3——不同之处在于这个函数自动挂载到了
window对象上,所以可以这么理解,当我们调用fn()时其实调用的事window.fn(),所以this就是window。console.log(fn === window.fn); // -> true -
如果符合上述多个规则,则越前面的规则会决定
this的值。 -
如果函数是一个
ES2015箭头函数,会忽略上述所有规则,this设置为它被创建时的上下文。为了找到this的值,需要找到函数被创建时的环境中this的值。const obj = {
value: 'abc',
createArrowFn: function() {
return () => console.log(this);
}
};
const arrowFn = obj.createArrowFn();
arrowFn(); // -> { value: 'abc', createArrowFn: ƒ }我们返回去看规则3,当我们调用
obj.createArrowFn()时,createArrowFn中的this就是obj对象,我们用.符号调用。如果我们在全局中创建一个箭头函数,this就是window。
应用规则
下面在几个例子中应用一下我们的规则。试一下通过两种不同的方式调用函数时 this 的值。
找到应用的规则
var obj = {
value: 'hi',
printThis: function() {
console.log(this);
}
};
var print = obj.printThis;
obj.printThis(); // -> {value: "hi", printThis: ƒ}
print(); // -> Window {stop: ƒ, open: ƒ, alert: ƒ, ...}
obj.printThis() 很显然应用的是规则3——使用 . 符号。 print() 应用了规则4,在调用 print() 时,我们没有使用 new 、 bind/call/apply 或 . 符号,所以这里的 this 是全局对象 window 。
多重规则应用
如上文提到,当应用多个规则时,优先应用前面的规则。
var obj1 = {
value: 'hi',
print: function() {
console.log(this);
},
};
var obj2 = { value: 17 };
如果规则2和3同时应用,规则2优先。
obj1.print.call(obj2); // -> { value: 17 }
如果规则1和3同时应用,规则1优先。
new obj1.print(); // -> {}
关于工具库
一些 JavaScript 库有时候会在函数中主动绑定它认为最有用的内容到 this 上。比如在 JQuery中,在触发事件时 DOM 元素被绑定到了 this 上。在使用工具库时发现取值不符合上述规则时,请查看库文档。很可能使用了 bind 语法。