翻译自文章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
语法。