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