V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pythonee
V2EX  ›  程序员

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

  •  
  •   pythonee · 2013-07-13 10:36:00 +08:00 · 3935 次点击
    这是一个创建于 4151 天前的主题,其中的信息可能已经有所发展或是发生改变。
    都说闭包内部存的是引用而不是复制,那么且看

    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更改,闭包也能反映出来,这是什么情况
    第 1 条附言  ·  2014-01-22 18:05:04 +08:00
    11 条回复    1970-01-01 08:00:00 +08:00
    anjianshi
        1
    anjianshi  
       2013-07-13 10:50:06 +08:00
    楼主,看看这篇文章,应该能解答你的疑惑

    http://www.xiaoxiaozi.com/2009/05/22/588/
    hardway
        2
    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
        3
    pythonee  
    OP
       2013-07-13 11:25:57 +08:00
    @hardway
    @anjianshi

    不是吧,最后又归结到这个原因上哦,看来所有语言都差不多的,对象的复制成本太高
    2code
        4
    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
        5
    pythonee  
    OP
       2013-07-13 11:36:47 +08:00
    @anjianshi

    啊,我试了一下,对于name和age仍然是复制的行为,list表现为引用的行为,我觉得基本和对象类型不是重点
    finian
        6
    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
        7
    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
        8
    pythonee  
    OP
       2013-07-14 13:33:53 +08:00
    @anjianshi

    主要是想表达传值还是传引用的意思
    heroicYang
        9
    heroicYang  
       2013-07-14 14:42:43 +08:00
    看着这个代码格式,完全没有继续读的欲望啊。。。
    前段时间 @alay9999 同学才整理过如何贴代码的教程,LZ还是看看先吧: /t/74249
    heroicYang
        10
    heroicYang  
       2013-07-14 14:54:53 +08:00
    你的第一段代码问题并不出在闭包身上,而是如4楼的 @2code 所说那样。
    FrankFang128
        11
    FrankFang128  
       2014-01-22 19:26:13 +08:00
    不缩进的代码读不懂,估计楼主已经知道原因了。
    复制引用类型不是代价大,是有坑。尤其是循环引用。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1571 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:10 · PVG 01:10 · LAX 09:10 · JFK 12:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.