0

Can someone explain me why the code below doesn't print the numbers from 0 to 2 on this.closure unless I do new crazyClosure(i) or 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);
}

Is like if the closure would be attached to this instead of to the crazyClosure function itself.

I would expect to see:

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

but I get

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

Adding another example according to answers. In this example I have 2 functions on this which is window that get overridden every time I call crazyClosure though I get the result I was expecting when calling expected but I get the result you guys say I should get when calling unexpected. According to your explanation I still don't understand how that can happen.

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);
}

I get:

init 0
init 1
init 2
this.expected 0
this.unexpected 2
this.expected 1
this.unexpected 2
this.expected 2
this.unexpected 2

I need to understand this!!! Thanks

4

2 回答 2

3

您应该尝试将闭包的概念与 Javascript 对象的概念分开。

它们完全不相关,在学习时混合它们会带来比需要更多的混乱。

在您的代码中,问题在于它crazyClosure看起来像一个构造函数,但被称为函数,因此它不会接收this要处理的对象,因此可以使用this设置为window.

结果是这self也将被绑定,window并且调用self.closure()将调用您创建的循环的最后一个闭包。所有三个超时事件都将调用最后一个闭包,因为 Javascript 在循环终止之前不会执行超时。

也请帮自己一个忙,立即离开“Javascript 闭包被破坏”的态度。如果您的第一个想法是其他人的代码被破坏了,那么您将不会在编程中走得太远。请不要把它当成个人,也不要认为这是对你提问的礼貌程度的咆哮。认为你的代码是正确的而其他人的代码(库、编译器、操作系统)是错误的心理习惯是真正的杀手……不要落入那个陷阱。

编辑

第二个例子更复杂,但结果输出仍然是 Javascript 实现应该做的。

如前所述,每次通话一this会是window因为new通话时没有使用话务员crazyClosure

这两个“方法”expected最终unexpected将成为全局函数,您可以检查这一点,因为执行后您将能够expected()直接从外部调用crazyClosure

然而,虽然在内部crazyClosure传递的值setTimeout每次都会不同,因为全局expected将被一个可以访问局部count变量的新闭包覆盖。这个expected全局变量稍后会被覆盖,但是当该值已经被传递给setTimeout并且 Javascript 运行时已经将它存储起来以便在 10 毫秒后调用它时。

然而,所有这三个闭包在他们的代码调用self.unexpected中,当发生这种情况时,这个全局已经被覆盖了三次,所以所有三个不同的闭包“预期”都会调用同一个闭包“意外”(最后一个),因为当超时触发时这是当前存储在window.unexpected.

于 2013-10-03T05:57:38.740 回答
1

如果您不使用newthis则函数的开头crazyClosure是封闭上下文,而不是新对象。因此,所有调用都以相同的方式结束调用闭包函数selfwindow如果您的代码未嵌入)。

每次调用

this.closure = function () {
    console.log("this.closure", count);
};

相当于

window.closure = function () {
    console.log("this.closure", count);
};

从而取代了先例功能。你最终只有一个 window.closure函数,嵌入了最后一个值,这就是你调用 3 次的独特函数setTimeout

于 2013-10-03T05:39:26.700 回答