关于闭包的问题

    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返回出去获取不到最新的值咧。

小白上路,望大神指导~

阅读 3.1k
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。

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

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

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

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

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

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

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

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

  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是会指向同一块内存地址的。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题