JS中的闭包及其应用

246 阅读3分钟

闭包的概念

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。这一段描述是MDN社区对闭包的定义,也就是说在子层作用域内存在访问父层作用域中的引用,闭包就会产生。闭包是函数式编程产生的特有的概念。

闭包的形成时机

由上面的概念,我们可以推导出闭包的形成与作用域有关。具体有啥关系呢,可从下图看出结论。

58206cd67bc7c8709307e7a25c3bb0a.png

上图的代码逻辑是:先声明一个函数fa以及作用域内变量a,fa作用域下再声明函数fb以及变量b,变量b捆绑了对父作用域变量a的引用。函数fb并没有开始执行,我们打印了fb的对象,可以看到[Scopes](作用链)属性中存在Closure对象。

也就是说,函数未执行的声明阶段,闭包对象已经产生了,闭包对象是作用域链数组中的一个元素,更具体的表述是,在代码的预解释阶段,闭包已经形成

闭包的应用场景以及应用演变过程

用循环生成任务队列时

  for(var i = 0; i < 5; i++){
      setTimeout(() => {
          console.log(i)
      },1000)
  }

以上代码表示,循环发布5个宏任务,将这5个任务加入到任务队列中,并且执行。任务队列执行时,for循环已经完成循环。所以取到的变量i为5。具体的原因是,用var语法声明变量时,并不能产生一个隔离的作用域。或者说,for(){}语法并不能产生作用域。所以我们要给代码创建一个隔离的作用域,才能达到我们的目的。

  for(var i = 0; i < 5; i++){
      setTimeout(((i) => {
          console.log(i)
      })(i),1000)
  }

创建一个自执行函数,便达到了生成隔离作用域的效果。

两种的代码运行效果如下

21ddf30202fc85ed55a39f2e3c2ab1b.png

4d657ab15fe9cdbb7f8f4ef06ad7453.png

用闭包的方式实现单例模式

    const Person = (() => {
        let instance = null;
        const name = "white";
        const age = 20;

        const init = () => ({
            getName : () => name,
            getAge : () => age
        })

       return {
            getInstance: () => {
                if(!instance){
                    instance = init();
                } 
                return instance; 
            }
       }

    })();

    const a = Person.getInstance();
    const b = Person.getInstance();

    console.log(a === b) //结果为true
    

单例模式在react的应用有很多,比如一些状态管理器、路由对象等等。其实现原理便是采用的单例模式来创建。

用闭包实现模块化,达到数据共享

  // 模拟react的useState钩子
  const useState = (inital) => {
      let value = inital;
      const setValue = val => value = val;
      const getValue = () => value;
      
      return [getValue, setValue]
  }

image.png

用自执行函数生成闭包,达到类似JQuery模块化的效果

//自定义jquery原型
 (function(module){
  JQuery = JQuery.prototype = {
      constructor: JQuery
      //自定义jquery属性
     //some attribute...
  }
   //给window对象添加全局属性$ 和 JQuery
   module.JQuery = module.$ = JQuery
 })(window)

闭包既是提供给我们JS开发者的一套编程思想。也是JS走向模块化的基石。

以上就是闭包的概念以及一些简单的应用场景的描述。实际应用上闭包是函数式编程最为广泛的应用。

未完待续...