这是一个JavaScript闭包错误吗?

时间:2013-10-03 05:36:09

标签: javascript closures

有人可以解释一下为什么下面的代码不会打印从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

我需要明白这一点!感谢

2 个答案:

答案 0 :(得分:3)

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

他们是完全不相关的,并且将它们混合在一起,而学习会带来比所需更多的混乱。

在你的代码中,问题是crazyClosure似乎是一个构造函数,但是作为一个函数被调用,所以它不会收到一个this对象来处理,因此可以使用{{1} }设置为this

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

请你自己帮忙,立即离开“Javascript关闭破了”的态度。如果您第一次认为其他人的代码被破坏了,那么您就不会在编程方面走得太远。请不要亲自接受,并且不要认为你提出问题是多么礼貌。认为你的代码是正确的以及其他人的代码(库,编译器,操作系统)错误的心理习惯是一个真正的杀手......不要陷入那个陷阱。

修改

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

self.closure()之前所说的每次调用都是this,因为在调用window时未使用new运算符。

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

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

所有这三个闭包但是在他们的代码调用expected中,当发生这种情况时,这个全局已经被覆盖了三次,所以所有三个不同的闭包“预期”将调用相同的闭包“意外”(最后一个)因为当超时触发它时,它是当前存储在setTimeout中的内容。

答案 1 :(得分:1)

如果您不使用newthis函数开头的crazyClosure是封闭的上下文,而不是新对象。所以所有调用都在同一个self上调用闭包函数(如果你的代码没有嵌入,那么window)。

每次致电

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

相当于

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

因此取代了先前的功能。您最终只能使用一个 window.closure函数,嵌入最后一个值,这就是您使用setTimeout调用三次的独特函数。