Javascript 为什么对象是';函数调用中捕获的值?

Javascript 为什么对象是';函数调用中捕获的值?,javascript,Javascript,此代码应在您单击图像时弹出带有图像编号的警报: for(var i=0; i<10; i++) { $("#img" + i).click( function () { alert(i); } ); } 你可以看到它在工作 它为什么有效?内存中仍然只有一个i对象,对吗?对象总是通过引用传递的,而不是复制的,因此自执行函数调用应该没有什么区别。两个代码段的输出应该相同。那么为什么要将i对象复制10次呢?它为什么有效 我觉得这个版本很有趣: 不工作: for(

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

for(var i=0; i<10; i++) {
    $("#img" + i).click(
        function () { alert(i); }
    );
}
你可以看到它在工作

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

我觉得这个版本很有趣:

不工作:


for(var i=0;i在第一个示例中,
i
只有一个值,它是
for
循环中使用的值。这样,当
for
循环结束时,所有事件处理程序都将显示
i
的值,而不是所需的值

在第二个示例中,在安装事件处理程序时,
i
的值被复制到
i2
函数参数中,对于函数的每次调用以及每个事件处理程序,都有一个单独的副本

那么这个,

(function (i2) {
    $("#img" + i2).click(
        function () { alert(i2); }
    );
 })(i);
创建一个新变量
i2
,该变量在每次单独调用函数时都有自己的值。由于javascript中有闭包,因此每个单独的事件处理程序都会保留
i2
的每个单独副本,从而解决您的问题

在第三个示例中,没有创建
i
的新副本(它们都引用了
for
循环中相同的
i
),因此其工作原理与第一个示例相同。

这是因为您正在调用一个函数,并向它传递一个值

使用此函数,您希望它对传入它的任何
val
发出警报,对吗?在循环中调用它时也可以这样做:

for (var i = 0; i < 10; i++) {
    attachClick(i);
}
for(变量i=0;i<10;i++){
附件(一);
}
这:

for(变量i=0;i<10;i++){
(功能(val){
$(“#img”+val)。单击(
函数(){alert(val);}
);
})(i) );
}

只是上面的内联声明。您正在声明一个匿名函数,其特征与上面的
attachClick
相同,并立即调用它。通过函数参数传递值的行为会中断对
i
变量的任何引用。

代码1和代码3不起作用,因为
i
是一个变量,每个循环中的值都会更改。在循环结束时,将
10
分配给
i

为了更清楚,请看这个例子

for(var i=0; i<10; i++) {

}

alert(i)
for(var i=0;i运行下一个示例:

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

i++;

用于(var i=0;i对deceze的答案投了赞成票,但我想尝试一个更简单的解释。闭包之所以有效,是因为javascript中的变量是函数作用域。闭包创建了一个新的作用域,通过将
i
的值作为参数传入,您在新的作用域中定义了一个局部变量
i
。没有闭包,您定义的所有单击处理程序都在同一范围内,使用相同的
i
。上一个代码段不起作用的原因是没有本地
i
,因此所有单击处理程序都在查找定义了
i
的最近父上下文

我认为另一件可能让你困惑的事情是这个评论

对象总是通过引用传递的,而不是复制的,因此自执行函数调用应该没有什么区别

这对于对象是正确的,但对于基本值(例如数字)则不正确。这就是为什么可以定义一个新的本地
i
。为了演示,如果您做了一些奇怪的事情,比如在数组中包装i的值,那么闭包将不起作用,因为数组是通过引用传递的

// doesn't work
for(var i=[0]; i[0]<10; i[0]++) {
    (function (i2) {
        $("#img" + i2[0]).click(
            function () { alert(i2[0]); }
        );
    })(i);
}
//不起作用

对于(var i=[0];i[0]大多数答案都是正确的,因为将对象作为函数参数传递会破坏闭包,从而允许我们从循环中为函数赋值。但我想指出为什么是这种情况,而且这不仅仅是闭包的特殊情况

你看,javascript向函数传递参数的方式与其他语言有点不同。首先,它似乎有两种方式,这取决于它是原语值还是对象。对于原语值,它似乎通过值传递,而对于对象,它似乎通过引用传递

// doesn't work
for(var i=[0]; i[0]<10; i[0]++) {
    (function (i2) {
        $("#img" + i2[0]).click(
            function () { alert(i2[0]); }
        );
    })(i);
}
javascript如何传递函数参数 事实上,javascript所做的真正解释解释了这两种情况,以及为什么它只使用一种机制来打破闭包

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

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

var i = 1;
function increment (n) { n = n+1 };
increment(i); // we would expect i to be 2 if i is a reference
但在javascript中,情况并非如此:

console.log(i); // i is still 1
这是一个经典的传递值,不是吗

通过参考? 但是,等等,对于对象,情况就不同了:

var o = {a:1,b:2}
function foo (x) {
    x.c = 3;
}
foo(o);
如果参数按值传递,我们希望
o
对象保持不变,但:

console.log(o); // outputs {a:1,b:2,c:3}
这是经典的按引用传递。所以我们有两种行为,这取决于我们传递的是基元类型还是对象

等等,什么? 但是等一下,看看这个:

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}
什么!那不是通过引用传递的!值是不变的

这就是为什么我称它为引用的按副本传递。如果我们这样想,一切都是有意义的。我们不需要认为原语在传递到函数中时有特殊行为,因为对象的行为方式是相同的。如果我们试图修改对象,变量指向它,那么它就像按引用传递一样,但如果我们试图修改引用本身,那么它的工作原理就像传递值一样

这也解释了为什么闭包是br
var i = 1;
function increment (n) { n = n+1 };
increment(i); // we would expect i to be 2 if i is a reference
console.log(i); // i is still 1
var o = {a:1,b:2}
function foo (x) {
    x.c = 3;
}
foo(o);
console.log(o); // outputs {a:1,b:2,c:3}
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}
var i = 1;
function bat (n) { n.hello = 'world' };
bat(i);
console.log(i.hello); // undefined, i is unchanged
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);
}
var o;
setTimeout(function () { o = {value: 5}; }, 100);
setTimeout(function () { alert(o.value) }, 1000);