我目前正在学习ES6标准,对于箭头功能部分,我发现了一个令人困惑的问题如下:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
以上是ES6标准,ES5如下
function foo() {
setTimeout(function(){
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
In ES5, the output is id:21
In ES6, the output is id:42
我能理解ES6的结果。但与ES5的结果非常混淆。 foo()函数的调用站点是foo.call({id:42})
,因此调用对象是{id:42},但是当它执行时,调用对象变为window。怎么样?
答案 0 :(得分:1)
这里有几件。首先,要了解this
本质上是一个由调用函数的东西自动设置的参数。这对理解至关重要。因此,在这种情况下,setTimeout
负责调用您的函数,它基本上是fn.call(undefined)
,因此不会传入明确的this
。
接下来,您的示例:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
VS
function foo() {
setTimeout(function(){
console.log('id:', this.id);
}, 100);
}
箭头功能案例基本上是:
function foo() {
var _this = this;
setTimeout(function(){
console.log('id:', _this.id);
}, 100);
}
因此,在两种情况下比较this
,第一种情况使用调用了this
的{{1}},并且您的示例代码有效,因为您需要foo
显式设置比。第二个示例使用foo.call({ id: 42 });
传递的this
,如上所述setTimeout
。
但是,从undefined
传入的this
可能实际上不是最终在函数内公开的.call
。
this
直接传递,如果它是一个对象,但如果不一个对象,this
是全局对象(浏览器中的this
,节点中的window
global
直接通过未更改的由于您的示例代码未标记为this
,这意味着您正在尝试此全局行为。这意味着你的两个例子基本上是这样的:
"use strict";
VS
function foo() {
setTimeout(function(){
console.log('id:', window.id); // reads "id" from the global object
}, 100);
}
这里的最后一块是你的
function foo() {
var _this = this;
setTimeout(function(){
console.log('id:', _this.id); // reads "this" from `foo.call(...)`
}, 100);
}
属于全局范围,因此基本上是var id = 21;
。因此,这个价值贯穿始终。
我推荐的主要内容:
window.id = 21
声明就不会在var
上结束。如果您使用CommonJS或Webpack的ES6模块语法等模块系统,则会自动执行此操作。如果您这样做,您的代码将按原样window
记录,因为undefined
不存在。window.id
标记您的代码。这通常很容易。如果你正在使用CommonJS,只需将它放在文件的顶部,如果你正在使用ES6模块,它将被自动添加,因为ES6模块必须是严格的。如果您这样做,非箭头回调中的"use strict";
将为this
,因此undefined
会引发错误。答案 1 :(得分:0)
ES5:'this'被验证,直到函数实际调用(即function()或'new'关键字)。在你的情况下,setTimeout中的回调函数实际上是由window对象调用的(因此你的'this'原来是window对象),并且window对象在你声明之后有'a'属性。这就是它打印21的原因。
ES6:宣布你的功能后,'this'会被验证。因此'this'指向'call'函数中的任何内容。当然你可以尝试调用(窗口),然后你会看到21按预期打印出来,因为窗口有'a = 21'属性。
这是一个非常好的问题,我希望上面的解释有所帮助:)