有人可以解释一下为什么下面的代码不会打印从0到2的数字
this.closure
,除非我new crazyClosure(i)
或crazyClosure.call({}, i)
?
var crazyClosure = function (count) {
var self = this;
function closure () {
console.log("closure", count);
};
this.closure = function () {
console.log("this.closure", count);
};
console.log("init", count);
setTimeout(function () {
console.log('');
console.log("call", count);
closure();
self.closure();
}, 0);
}
for (var i = 0; i < 3; i++) {
crazyClosure(i);
}
如果闭包是附加到this
而不是附加到crazyClosure
函数本身。
我希望看到:
init 0
init 1
init 2
call 0
closure 0
this.closure 0
call 1
closure 1
this.closure 1
call 2
closure 2
this.closure 2
但我得到
init 0
init 1
init 2
call 0
closure 0
this.closure 2
call 1
closure 1
this.closure 2
call 2
closure 2
this.closure 2
根据答案添加另一个例子。在这个示例中,我在this
上有2个函数,window
每次调用crazyClosure
时都会被覆盖,虽然我得到了调用expected
时我期望的结果但是我得到了你们在调用unexpected
时应该得到的结果。根据你的解释,我仍然不明白这是怎么发生的。
function crazyClosure (count) {
var self = this;
this.expected = function () {
console.log("this.expected", count);
self.unexpected();
};
this.unexpected = function () {
console.log("this.unexpected", count);
};
console.log("init", count);
setTimeout(self.expected, 10);
}
for (var i = 0; i < 3; i++) {
crazyClosure(i);
}
我明白了:
init 0
init 1
init 2
this.expected 0
this.unexpected 2
this.expected 1
this.unexpected 2
this.expected 2
this.unexpected 2
我需要明白这一点!感谢
答案 0 :(得分:3)
你应该尝试将闭包的概念与Javascript对象的概念分开。
他们是完全不相关的,并且将它们混合在一起,而学习会带来比所需更多的混乱。
在你的代码中,问题是crazyClosure
似乎是一个构造函数,但是作为一个函数被调用,所以它不会收到一个this
对象来处理,因此可以使用{{1} }设置为this
。
结果是window
也将与self
绑定,并且调用window
将调用您创建的循环的最后一次闭包。所有三个超时事件都将调用最后一个闭包,因为在循环终止之前Javascript不会执行超时。
第二个例子更复杂,但结果输出仍然是Javascript实现应该做的。
self.closure()
之前所说的每次调用都是this
,因为在调用window
时未使用new
运算符。
两个“方法”crazyClosure
和expected
最终将成为全局函数,您可以检查这一点,因为执行后您可以直接从外部unexpected
调用expected()
1}}。
然而,在crazyClosure
内部,传递给crazyClosure
的值每次都会有所不同,因为全局setTimeout
将被一个可以访问本地{{1}的新闭包覆盖变量。这个expected
全局变量将在稍后被覆盖,但是当该值已经传递给count
并且Javascript运行时已将其存储起来以便在10毫秒后调用它。
所有这三个闭包但是在他们的代码调用expected
中,当发生这种情况时,这个全局已经被覆盖了三次,所以所有三个不同的闭包“预期”将调用相同的闭包“意外”(最后一个)因为当超时触发它时,它是当前存储在setTimeout
中的内容。
答案 1 :(得分:1)
如果您不使用new
,this
函数开头的crazyClosure
是封闭的上下文,而不是新对象。所以所有调用都在同一个self
上调用闭包函数(如果你的代码没有嵌入,那么window
)。
每次致电
this.closure = function () {
console.log("this.closure", count);
};
相当于
window.closure = function () {
console.log("this.closure", count);
};
因此取代了先前的功能。您最终只能使用一个 window.closure
函数,嵌入最后一个值,这就是您使用setTimeout
调用三次的独特函数。