JavaScript闭包丢失了这个引用

JavaScript closure loses this reference?

本文关键字:引用 闭包 JavaScript      更新时间:2023-09-26

考虑以下代码:

<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. log)Foo, Foo)应该总是输出相等的数字(i)。

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

2 0
2 1
2 2

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

在您的函数wtf中,在变量foo中捕获的值是一个标量-实际数值。由于它在创建函数时捕获标量值,因此该函数在值0,1,2等处关闭。这就是为什么它似乎在增加。

然而,当_this被捕获时,它被捕获为对单个实例的引用,该实例有一个名为foo的字段。因此,使用_this。

总结一下,像整数这样的值类型是由它们的值捕获的,而对象是由它的引用捕获的。

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

然后,当调用wtf()时,每个wtf()闭包中的_this变量都是相同的——它们都指向window

查看这个jsfiddle: http://jsfiddle.net/8cyHm/

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

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

您必须理解的第一件事是,您正在调用没有new前缀的test函数,这使得函数内部的this指针指向window对象

<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>

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

现在,当您单击div时,它实际上有3个函数侦听其单击事件(test函数内的3个内联wtf函数)(每次调用test函数都会创建一个新的wtf内联函数)。这些内联函数中的每一个都在本地变量foo上读取其值,然后每个函数分别具有1,2,3的值。

所以,它是这样工作的:

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

这条线

var _this = this;

仅仅将全局对象存储到局部变量_this中。如果在严格模式环境中调用函数test,则this将是undefined,代码将抛出错误。然而,由于它不是严格模式,this值指的是全局对象。

顺便说一句,i变量是在全局代码中声明的,这使它成为一个全局变量(全局属性)。

这条线

var foo = _this.foo = i++;

i的当前值赋给本地变量foo_this.foo。由于_this是对全局对象的引用,所以属性foo是一个全局属性(就像i一样)。

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

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

现在,当单击DIV时,将执行三个wtf函数。每个函数都将输出_this.foo的值,即2。然而,这些wtf函数中的每一个都通过闭包捕获了一个不同的 foo变量。"foo"的值分别为"0"、"1"、"2"。

如果你在Chrome中测试,你可能会在console.log中遇到一个bug。参见:Javascript Funky array mishap

尝试更改为:

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