javascript的闭包行为怎么这么诡异?

2013-07-13 10:36:00 +08:00
 pythonee
都说闭包内部存的是引用而不是复制,那么且看

function Container( properties ) {
var objthis = this;
for ( var i in properties ) {
(function(){
var t = properties[i];
objthis[ "get" + i ] = function() {return t;};
objthis[ "set" + i ] = function(val) {t = val;};
})();
}
}

var prop = {Name : "Jim", Age : 13};
var con = new Container(prop);
console.log(con.getName());

con.setName("Lucy");
console.log(con.getName());
console.log(prop.Name);


输出却是
Jim
Lucy
Jim

也就是Containner的setName方法没有改变prop属性了,这不是复制吗?OK,我们看正常一点的


function Foo(list){
var a = list;
return function(){
a.push(1);
console.log(a);
}
};

var l = [];
var f = Foo(l);
f();
f();
l.push(2);
f();

这里的输出却是
[1]
[1, 1]
[1, 1]
[1, 1, 2, 1]

这又是明显的保存的引用的行为,也就是闭包的push结果, 外部的list可以看到,而外部的list更改,闭包也能反映出来,这是什么情况
3934 次点击
所在节点    程序员
11 条回复
anjianshi
2013-07-13 10:50:06 +08:00
楼主,看看这篇文章,应该能解答你的疑惑

http://www.xiaoxiaozi.com/2009/05/22/588/
hardway
2013-07-13 10:57:12 +08:00
你第一个例子测试的都是基本值,如果这样就对了,因为var t = properties[i];对于基本类型会复制
var prop = {Name : "Jim", Age : 13, Cards: []};
var con = new Container(prop);

con.getCards().push(1);

console.log(con.getCards());
console.log(prop.Cards);
pythonee
2013-07-13 11:25:57 +08:00
@hardway
@anjianshi

不是吧,最后又归结到这个原因上哦,看来所有语言都差不多的,对象的复制成本太高
2code
2013-07-13 11:32:45 +08:00
问题在这一句上:
var t = properties[i];

你去掉这一句,再把下面t出现的地方都改为properties[i] 就不会了

因为你的property[i] 是原始值类型的(字符串),所以你这一次赋值,是传值,而不是传引用

用下面的方法测试:

a = {Name:'Jim'}
n = a.Name;

n = 'Lucy';
console.log(a.Name);

a.Name = 'Lucy';
console.log(a.Name);
pythonee
2013-07-13 11:36:47 +08:00
@anjianshi

啊,我试了一下,对于name和age仍然是复制的行为,list表现为引用的行为,我觉得基本和对象类型不是重点
finian
2013-07-13 12:23:58 +08:00
@pythonee properties的Name和Age是基本值,于是,var t = properties[i]; 是复制行为,list是对象,var a = list; 是引用行为。第一个例子,按照你的逻辑,应该是酱紫的:

function Container( properties ) {
var objthis = this;
for ( var i in properties ) {
(function(){
var t = properties[i];
objthis[ "get" + i ] = function() {return t;};
objthis[ "set" + i ] = function(val) {properties[i] = val;};
})();
}
}
anjianshi
2013-07-13 17:33:02 +08:00
@pythonee
不好意思,你在5楼说的那句话我理解不能 -_- ||

其实这个问题和闭包无关,"闭包内部存的是引用" 这句话本身就是个伪命题,可能是楼主把哪个人说的话理解错了吧。
闭包就是一个类似屏障的东西,闭包里能访问外部的变量,但外部没法直接访问闭包里的变量,它不牵扯到“引用”的问题。

以前看过一个文章对这个问题讲得很生动,但现在找不到了。我把大意说一下吧:
想象 javascript 中的每一个对象就是一个人(包括字符串,数组,object),变量名就是它的名字
a = {"id" : 1} 就是给 {"id" : 1} 起了个名字,叫 "a"
b = a 就是给 {"id" : 1} 起了个外号,叫 "b"
现在无论你调用 a 还是 b ,得到的都是 {"id" : 1} 这个人
a.foo = 'bar'
alert(b.foo) // 输出 'bar'

那么,如果我们这样操作:b = {"id" : 2} 这代表什么呢?
这个操作同时带来两个效果:
1. 取消了 {"id" : 1} 的外号,现在他只有 "a" 这个一个名字了,"b" 这个外号给了别人
2. 给 {"id" : 2} 起了一个名字,也就是 "b"
现在你在调用 a 进行操作,就不会再对 b 有什么影响了
pythonee
2013-07-14 13:33:53 +08:00
@anjianshi

主要是想表达传值还是传引用的意思
heroicYang
2013-07-14 14:42:43 +08:00
看着这个代码格式,完全没有继续读的欲望啊。。。
前段时间 @alay9999 同学才整理过如何贴代码的教程,LZ还是看看先吧: /t/74249
heroicYang
2013-07-14 14:54:53 +08:00
你的第一段代码问题并不出在闭包身上,而是如4楼的 @2code 所说那样。
FrankFang128
2014-01-22 19:26:13 +08:00
不缩进的代码读不懂,估计楼主已经知道原因了。
复制引用类型不是代价大,是有坑。尤其是循环引用。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/75627

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX