为什么在函数调用中捕获对象的值? [英] Why are objects' values captured inside function calls?

查看:107
本文介绍了为什么在函数调用中捕获对象的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码应该在您单击时弹出一个带有图像编号的警报:

This code is supposed to pop up an alert with the number of the image when you click it:

for(var i=0; i<10; i++) {
    $("#img" + i).click(
        function () { alert(i); }
    );
}

你可以看到它无法在 http://jsfiddle.net/upFaJ/ 。我知道这是因为所有的click-handler闭包都引用了同一个对象 i ,因此每个处理程序在触发时会弹出10。

You can see it not working at http://jsfiddle.net/upFaJ/. I know that this is because all of the click-handler closures are referring to the same object i, so every single handler pops up "10" when it's triggered.

然而,当我这样做时,它工作正常:

However, when I do this, it works fine:

for(var i=0; i<10; i++) {
    (function (i2) {
        $("#img" + i2).click(
            function () { alert(i2); }
        );
    })(i);
}

你可以看到它在 http://jsfiddle.net/v4sSD/

为什么会有效?内存中仍然只有一个 i 对象,对吧?对象总是通过引用传递,而不是复制,因此自执行函数调用应该没有区别。两个代码片段的输出应该相同。那么为什么 i 对象被复制10次?为什么会这样?

Why does it work? There's still only one i object in memory, right? Objects are always passed by reference, not copied, so the self-executing function call should make no difference. The output of the two code snippets should be identical. So why is the i object being copied 10 times? Why does it work?

我觉得这个版本的不是很有趣工作

for(var i=0; i<10; i++) {
    (function () {
        $("#img" + i).click(
            function () { alert(i); }
        );
    })();
}

似乎将对象作为函数参数传递完全不同。

It seems that the passing of the object as a function parameter makes all the difference.

编辑:好的,所以上一个例子可以用原语解释( i )通过值传递给函数调用。但是这个使用真实对象的例子呢?

OK, so the previous example can be explained by primitives (i) being passed by value to the function call. But what about this example, which uses real objects?

for(var i=0; i<5; i++) {
    var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
    toggler.click(function () { toggler.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
    $("#container").append(toggler);
}

不工作: http://jsfiddle.net/Zpwku/

for(var i=0; i<5; i++) {
    var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
    (function (t) {
        t.click(function () { t.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
        $("#container").append(t);
    })(toggler);
}

工作: http://jsfiddle.net/YLSn6/

推荐答案

大多数答案是正确的,因为传递一个对象作为一个函数参数打破了一个闭包,从而允许我们从一个循环中将事物分配给函数。但是我想指出为什么就是这种情况,这不仅仅是闭包的特殊情况。

Most of the answers are correct in that passing an object as a function parameter breaks a closure and thus allow us to assign things to functions from within a loop. But I'd like to point out why this is the case, and it's not just a special case for closures.

你看, javascript将参数传递给函数的方式与其他语言有点不同。首先,它似乎有两种方法可以根据天气来实现,它是一个原始值或一个对象。对于原始值,似乎传递值,对于对象,它似乎通过引用传递。

You see, the way javascript passes parameters to functions is a bit different form other languages. Firstly, it seems to have two ways of doing it depending on weather it's a primitive value or an object. For primitive values it seems to pass by value and for objects it seems to pass by reference.

实际上,javascript所做的真正解释只能用一种机制来解释这两种情况,以及它为什么会破解闭包。

Actually, the real explanation of what javascript does explains both situations, as well as why it breaks closures, using just a single mechanism.

javascript实际上通过参考副本传递参数。也就是说,它创建了对参数的另一个引用,并将该新引用传递给函数。

What javascript does is actually it passes parameters by copy of reference. That is to say, it creates another reference to the parameter and passes that new reference into the function.

假设javascript中的所有变量都是引用。在其他语言中,当我们说变量是一个引用时,我们希望它的行为如下:

Assume that all variables in javascript are references. In other languages, when we say a variable is a reference, we expect it to behave like this:

var i = 1;
function increment (n) { n = n+1 };
increment(i); // we would expect i to be 2 if i is a reference

但是在javascript中,它不是case:

But in javascript, it's not the case:

console.log(i); // i is still 1

这是一个经典的价值传递不是吗?

That's a classic pass by value isn't it?

但等等,对于对象它是一个不同的故事:

But wait, for objects it's a different story:

var o = {a:1,b:2}
function foo (x) {
    x.c = 3;
}
foo(o);

如果参数按值传递,我们期望 o 对象不变但是:

If parameters were passed by value we'd expect the o object to be unchanged but:

console.log(o); // outputs {a:1,b:2,c:3}

这是经典的参考传递那里。所以我们有两种行为取决于我们传递原始类型或对象的天气。

That's classic pass by reference there. So we have two behaviors depending on weather we're passing a primitive type or an object.

但是等一下,看看这个:

But wait a second, check this out:

var o = {a:1,b:2,c:3}
function bar (x) {
    x = {a:2,b:4,c:6}
}
bar(o);

现在看看会发生什么:

console.log(o); // outputs {a:1,b:2,c:3}

什么!这不是通过参考传递!值不变!

What! That's not passing by reference! The values are unchanged!

这就是我称之为通过参考副本的原因。如果我们这样思考,一切都是有道理的。传递给函数时,我们不需要将基元视为具有特殊行为,因为对象的行为方式相同。如果我们尝试修改变量指向的对象,那么它就像传递引用一样,但如果我们尝试修改引用本身,那么它就像传递值一样。

Which is why I call it pass by copy of reference. If we think about it this way, everything makes sense. We don't need to think of primitives as having special behavior when passed into a function because objects behave the same way. If we try to modify the object the variable points to then it works like pass by reference but if we try to modify the reference itself then it works like pass by value.

这也解释了为什么通过将变量作为函数参数传递来打破闭包。因为函数调用将创建另一个未被闭包绑定的引用,如原始变量。

This also explains why closures are broken by passing a variable as a function parameter. Because the function call will create another reference that is not bound by the closure like the original variable.

在我们结束之前还有一件事。之前我说过,它统一了原始类型和对象的行为。实际上没有,原始类型仍然不同:

One more thing before we end this. I said before that this unifies the behavior of primitive types and objects. Actually no, primitive types are still different:

var i = 1;
function bat (n) { n.hello = 'world' };
bat(i);
console.log(i.hello); // undefined, i is unchanged

我放弃了。对此没有任何意义。这就是它的方式。

I give up. There's no making sense of this. It's just the way it is.

这篇关于为什么在函数调用中捕获对象的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆