JS闭包

237 阅读2分钟

写在前面

JS变量作用域有两种:局部变量和全局变量。 JS有其特殊之处:函数内部可以直接读取全局变量(例1),在函数外部无法读取函数内的局部变量(例2)。

//例1
const n=999;
function f1(){
  console.log(n);
}
f1(); // 999

//例2
function f1(){
  const n=999;
}
console.log(n); // Uncaught ReferenceError: n is not defined

函数内部声明变量的时候,一定要使用let 或 const命令。如果不用的话,你实际上声明了一个全局变量!

如果想得到函数内部的局部变量,应该怎么办?

答案是: 在该函数内部再写一个函数。

//例3
function father(){
  const n = 999;
  function child(){
    console.log(n); //向上寻找到父亲中有变量n
  }
  return child; //将child作为结果返回,那么在father外就可以读到father内部的变量。
}
const result = father(); //result实际上就是child
result(); // 999

在例3中,father中所有的变量对child都是可见的。反过来child中的变量对father是不可见的。这就是JS语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

上面的child 就是 闭包。

什么是闭包?

闭包是一个函数,它那能够读取其他函数的局部变量。

对于JS来说,子函数能够读到父函数的局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包用来做什么?

  1. 访问函数内部的变量,如例3.
  2. 让变量保持在内存中,不会在函数调用结束后被垃圾回收机制回收。
//例4
function father(){
  let n=999;
  nAdd=function(){n+=1} //nAdd全局变量,匿名函数,相当于闭包,可以在函数外部读到函数的局部变量。
  function child(){
    console.log(n);
  }
  return child;
}
const result = father();//result实际上就是child,child被赋给全局变量result,导致child一直在内存中,而child依赖father,所以father也一直存在在内存中。
result(); // n = 999 
nAdd();  // n = 999+1
result(); // n = 1000

使用闭包需要注意什么?

  1. 内存消耗。解决办法:在退出函数之前,将不使用的局部变量全部删除。
  2. 不要随便改变函数内部的值。

参考文献

学习Javascript闭包(Closure)