js中的闭包(closure)

1,094 阅读3分钟

闭包的概念

概念:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

简单理解:声明函数过程中将环境信息和所属信息绑定在一起的数据结构。例如:

function test(){
    const a =  10;
    return function fn(){
        console.log(a);
    }
}
const myfn = test();
myfn(); // 10

上述fn函数执行后可以访问到test函数作用域里的a常量,这里就是典型的闭包。

作用域

​ 闭包的出现是和js的作用域有关系的。js中作用域主要分两种:1、全局作用域。2、局部作用域。

  • 全局作用域:就是变量在全局范围内都会起作用,js中全局变量都会挂在window全局对象上。例如:
<script>
  var a = 10;
	console.log(a);  // 10
	console.log(window.a); // 10
</script>
  • 局部作用域:变量在局域范围内起作用的作用域。

    • 函数作用域,可以通过函数来实现变量只在局部范围内起作用,例如:

       function test(){
         	//函数作用域变量只在函数内部起作用
              var a = 10;
              console.log(a);
          }
       test(); // 10
       console.log(a); // a is not defined
      
    • 作用域链:根据内部函数可以访问可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称为函数作用域链。作用域链查找原则是就近原则。

      function fn1 (){
              var num = 10;
              function fn2(){
                 var num = 20;
                  function fn3(){
                      console.log(num);
                  }
                  fn3();
              }
              fn2();
          }
        fn1();
      

      上述代码 num 会向上层作用域查找,如果没有找到会继续在上层作用域中查找num变量,这样就形成了作用域链。

    • 块级作用域:在“{}”代码块中起作用的作用域。例如:

      {
              // 变量只在“{}”代码块内起作用
              let  a = 10;
              console.log(a);
      }
      console.log(a);  // a is not defined
      

    闭包

    ​ 由于 声明函数过程中将环境信息和所属信息绑定在一起,也就是类似于上述作用域链中体现的,在函数作用域中仍然保存着对父级对象的引用就形成了闭包。利用闭包 可以实现js中一些高级功能。

    • 区分作用域

      • 利用函数作用域及闭包特性可以区分局部作用域防止变量污染

        (function (){
                var a = 10;
                function test(){
                    console.log(a); 
                }
                test(); // 10
        })()
        console.log(a); // a is not defined
        
    • 让外部可以访问内部变量

      function test(){
              var a = 10;
              return function(){
                  console.log(a);
              }
       }
       let testFn = test();
       testFn();
      

      上述代码可以访问到test内部a变量,同样test函数也是一个高阶函数。高阶函数:接受另一个函数作为参数的函数被称为高阶函数(Higher-Order Function)。

    • 缓存特性

      • 可以利用闭包来缓存变量,例如:
      const once = (fn)=>{
          let done = false;
          return function(){
              if(!done){
                  fn.apply(this,fn);
              }else{
                  console.log("this fn is already execute");
              }
              done = true;
          }
      }
      
      function test(){
          console.log("test...");
      }
      let myfn =  once(test);
      myfn();  // test...
      myfn();  // this fn is already execute
      

​上述代码 通过闭包实现高阶函数once 来实现函数执行结果的缓存。

综上所述,js中闭包可以给我们带来诸多便利。但是在使用缓存特性时候需要注意缓存的局部变量会常驻内存,会造成内存泄露。所以在使用的过程中可以手动的将不需要的局部变量删除。

参考文献
developer.mozilla.org/zh-CN/docs/…