源解 1 你怎么理解闭包?

155 阅读2分钟

背景

闭包是为了解决,子函数未被调用而父函数已经执行完毕了,需要销毁时发现子函数引用了自己作用域的变量

实现原理

大佬解释闭包原理,怎一个透彻了得!

咱一点点解释

  1. 为什么子函数没调用而父函数却执行完了?
    // 正常来说 顺序执行 没问题
   function father(){
        const a = 1;
        function child(){
            console.log(a);
        };
        child();
    }
    // 但是js 里 函数可以return出去!
  function father(){
        const a = 1;
       return function child(){
            console.log(a);
        };
        
    }
  let child = father();
  
   

child 就可以任意时候执行了
那么问题来了father 销毁吗? 销毁了的话,执行child()时, console.log(a) 打印 undefined 了, 这可不行啊.
于是便有了闭包

  • 通俗理解就是: 父函数:" 你需要a? 那我打包给你,你存起来带着走吧,我销毁了,不多占内存了".
  • 专业的术语就是: 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一 起,这样的组合就是闭包closure

闭包们储存在属性[[scopes]]里,复数也可说明打包的闭包并不会合并,如果你用到了父作用域的a,爷的x,祖师爷的y,那就会产生三个记录.

image.png

(图源自开头文章)
而需要注意的是无论是否引用外部变量,Global全局作用域总是会打包进去,这也是为什么函数无论在哪调用,都能访问到全局变量的原因

image.png (图源自开头文章)
2.不销毁父函数行吗?
不行,性能消耗太多,虽然闭包也会消耗内存,但相对而言肯定更优化,具体闭包是存在 堆 中的,所以使用不当可能会造成内存泄漏
3.都打包哪些变量?
只会打包在静态作用域链中用到的变量.这里的静态作用域链不是啥高级东西,解释一下,首先明确每一个函数,代码块等会形成一个作用域,而类似于函数的互相嵌套,作用域链也分父子,当在自己的作用域中找不到某个变量,就去问爸爸要,还没有就去问爷爷要,一直找到全局作用域,这就是作用域链.
4.Eval
eval()函数会打包所有作用域,因此会产生性能问题,这是因为无法检测会用到哪些调用,毕竟你传入一个字符串是动态的,只能全部打包,因此少用\

闭包的具体应用

防抖节流函数 
闭包可以用于在对象中创建私有变量
let 的实现原理,let用babel转换后就是用闭包实现的

如有错误,欢迎指出