JavaScript闭包失去了这个引用?

时间:2011-12-09 19:17:30

标签: javascript closures

请考虑以下代码:

<script>
    var i = 0;
    function test() {
        var _this = this;
        // foo and _this.foo are set to the same number
        var foo = _this.foo = i++;

        function wtf() {
            console.log(_this.foo, foo);
        }

        $("#thediv").click(wtf);
    };
    test();
    test();
    test();
</script>

似乎console.log(_this.foo,foo)应该总是输出相同的数字(i)。

但是单击div会输出3行(对于每个console.log调用):

2 0
2 1
2 2

似乎_this.foo总是指最后this.foo。为什么会这样?

5 个答案:

答案 0 :(得分:3)

在函数wtf中,变量foo中捕获的值是标量 - 实际数值。因为它在创建函数时捕获标量值,所以该函数正在关闭值0,1,2等。这就是它似乎递增的原因。

然而,当_this被捕获时,它被捕获为对具有名为foo的字段的单个实例的引用。因此,使用_this.foo,您指的是同一实例上的字段。

总而言之,像整数这样的值类型是通过它们的值捕获的,而对象是通过它的引用捕获的。

答案 1 :(得分:2)

运行test()时,this是对您的三个test()调用中每个window的引用,因此当您分配给{{{}时,实际上是在更新window.foo 1}}并在_this.foo中引用window.foo

然后,当调用console.log时,每个wtf()闭包中的_this个变量都相同 - 它们都指向wtf()

请参阅此jsfiddle:http://jsfiddle.net/8cyHm/

我在console.log中添加了一些额外的参数来显示正在发生的事情

答案 2 :(得分:1)

这是一个棘手的问题,:)

您必须要了解的首要事项是,您调用的test函数没有new前缀,这使得函数内的this指针引用{ {1}}对象

window

(plz阅读代码中的注释部分)

现在,当您点击<script> var i = 0; function test() { var _this = this; //** this referes to the window object var foo = _this.foo = i++; //** incrementing the global var and assigning that to a local var and a window.foo var function wtf() { console.log(_this.foo, foo); //** reads window.foo and its local var foo } $("#thediv").click(wtf); //** creates a new lister every time the test function gets called }; //** calling without the new keyword test(); //** creates foo-1, and wft-1 in memory, assigns foo-1=0; window.foo=0 test(); //** creates foo-2, and wft-2 in memory, assigns foo-2=1; window.foo=1 test(); //** creates foo-3, and wft-3 in memory, assigns foo-3=2; window.foo=2 </script> 时,其实际上有3个函数正在侦听其click事件(div函数内的3个内联wtf函数)(每次调用{ {1}}函数创建一个新的test内联函数)。这些内联函数中的每一个都在局部变量test上读取它们,每个变量分别具有值wtf

答案 3 :(得分:1)

所以,这就是它的工作原理:

test函数被调用三次。每次创建不同的 wtf函数对象并将其绑定到DIV作为其单击处理程序。这意味着在执行上述代码后,将有三个点击处理程序绑定到DIV。然后单击DIV时,将按顺序调用这三个处理程序。

这一行

var _this = this;

仅将全局对象存储到局部变量_this中。如果要在严格模式环境中调用函数testthis将为undefined并且代码将引发错误。但是,由于它不是严格模式,this值是指全局对象。

顺便说一下,i变量在全局代码中声明,使其成为全局变量(全局属性)。

这一行

var foo = _this.foo = i++;

i的当前值分配给局部变量foo_this.foo。由于_this是对全局对象的引用,因此属性foo是一个全局属性(就像i)。

现在,由于我们调用了test函数三次,因此还有三个单独的foo局部变量。这些变量中的每一个都在调用i函数时捕获test变量的值。因此,这三个foo变量的值为012。这些变量由三个wtf函数(分别)通过闭包捕获。 (第一个wtf函数捕获第一个foo变量,依此类推。)

但是,与foo变量不同,只有一个 foo全局属性。因此,在每次调用test函数之后,foo全局属性会递增。因此,在test被调用三次后,_this.foo的值为2。 (这是之前点击DIV。)

现在,当单击DIV时,将执行三个wtf函数。这些函数中的每一个都将打印_this.foo的{​​{1}}值。但是,每个2函数都通过闭包捕获了一个不同的 wtf变量。这三个foo变量的值分别为foo01

答案 4 :(得分:-2)

如果您在Chrome中进行测试,可能会遇到console.log中的错误。 请参阅:Javascript Funky array mishap

尝试更改为:

console.log(_this.foo + ' = ' + foo);