关于闭包的问题

    function test(){
        let name = 'init';
        function getName(){
            return name;
        }
        function setName(newName){
            name = newName;
        }
        return {
            getName:getName,
            setName:setName,
            name:name
        }
    }    
    let t1 = test();
    t1.setName('liujiapeng')
    console.log(t1.getName()) //liujiapeng
    console.log(t1.name); //init

为什么t1.name不能打印出修改后的值呢,我的理解是因为有闭包,因此t1创建的test上下文没有弹出,get和set函数中的name都是该上下文中的name。那为啥直接把name返回出去获取不到最新的值咧。

小白上路,望大神指导~

阅读 286
评论 2019-08-23 提问
    7 个回答
    function test(){
            let obj = {
                name: 'init'
            }
            
            function getName(){
                return obj.name;
            }
            function setName(newName){
                obj.name = newName;
            }
            return {
                getName:getName,
                setName:setName,
                obj: obj
            }
        }    
        let t1 = test();
        t1.setName('liujiapeng')
        console.log(t1.getName()) //liujiapeng
        console.log(t1.obj.name); //liujiapeng

    去查(对象)值的引用和(简单)值的复制。
    return 出去 name 的不是闭包里的那个 name,是它的 copy。

    评论 赞赏 2019-08-23

      闭包是一个函数,它hold住了外层函数作用域的变量。
      这样只要你引用了这个函数,那他hold的变量就会延迟释放。

      你这返回的是个对象,不能算闭包。

      评论 赞赏 2019-08-23

        你这两层的name作用域不一样哦,t1.name是取不到里面函数的name的

        评论 赞赏 2019-08-23

          每次调用 test() 函数, return 出去的都是一个全新的对象,它们之间是互不影响的

          如果 return 出去的对象属性值有引用类型,是会影响的

          其实就是简单数据类型和引用类型的问题

          评论 赞赏 2019-08-23

            楼主,你把t1打印出来就理解了,可以看到
            t1里面的name为init,而t1里面getName方法作用域链,最先读取的是test里的name.
            clipboard.png
            所以你第一个打印的是test里的name,而第二次打印的是t1对象的name。
            因为你是在将对象返出去后再改了test内的init值,t1对象内的name值仍为init.

            评论 赞赏 2019-08-23

              t1是返回的对象,t1.setName()t1.getName()返回的是父级作用域中的name,这个是闭包。
              t1.name是对象的一个属性,值是'init'。

              评论 赞赏 2019-08-23
                1. 首先,执行let t1 = test(),会产生一个可以窥探test函数词法作用域的闭包(closure),也可以说是创建了一个新的作用域,这个closure里面保存了一份test函数体内的变量,closure的结构伪代码类似:

                     {name:'init'}
                2. 其次,创建了一个json对象并赋值给局部变量t1,此例中该json对象的结构如下:

                  {
                     name:'init',//请注意:这里是传值,也就是传递的一份拷贝!而不是test函数内的那个name了!
                     setName:function(){},
                     getName:function(){}
                  }
                3. 执行t1.setName(newName)t1.getName(),当前作用域链上的对象(伪代码)如下

                  window <-- t1 <-- closure
                4. 很明显t1.setName(newName)t1.getName()test函数的词法作用域之外调用的,这种情况是闭包,而闭包的特性就是可以在函数的词法作用域之外保持对test函数内部变量name的访问和修改。
                5. t1.name只是对词法作用域里对象t1属性name的访问,而且也不是闭包的用法。换句话说,t1.getName()访问的是closure.name(伪代码),和t1.name不是指向同一块内存地址了。
                6. 注意:若name是引用类型,那么在执行test函数以创建一个json的赋值给t1时候,是传的引用,也即closure.namet1.name是会指向同一块内存地址的。
                评论 赞赏 2019-08-24
                  撰写回答

                  登录后参与交流、获取后续更新提醒