【前置知识】

  • JavaScript的作用域。
  • 内存空间

什么是闭包?

函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。 ——MDN

:::info 词法环境:在JavaScript中,每个运行的函数,代码块 {...} 以及整个脚本,都有一个称为 词法环境 的内部(隐藏)的关联对象。由局部变量和外部代码构成。 :::

正常情况下一个 外部函数 无法访问并获取到 内部函数 的变量。
原因是 内部函数 的局部变量,外部是无法访问的,内部却可以访问外部的变量。

例子1:

一般情况下,函数内部可以直接访问全局变量。

  1. function sayHi() {
  2. let name = "Pete";
  3. function fun2(){
  4. console.log("Hi, " + name);
  5. }
  6. }
  7. sayHi(); //显示“Hi,Pete”

另一方面,函数外部不可以访问函数内部的局部变量

  1. function sayHi() {
  2. console.log("Hi, " + name);
  3. function fun2(){
  4. let name = "Pete";
  5. }
  6. }
  7. sayHi(); //显示“Hi,Pete”

所以,按我的理解,闭包就是一个作用域有权访问另一个作用域的函数。上面举例的函数 fun2 可以访问函数 sayHi ,那么函数 fun2 就是闭包。

所以,闭包的一个前提是闭包必须是个函数!

闭包的用途是什么?

假如没有闭包

假如没有闭包,也就没有局部变量和全局变量的区别(或者说所有的变量都是全局变量)代码所有的角落都可以获取到所有的变量,万一某个程序员接手了一个没有闭包的大项目,该程序员就必须考虑自己写的变量名有没有和旧的变量名冲突!

所以闭包的用途就是防止全局污染。通过隐藏变量,实现私有数据,减少全局变量,尽可能的减少全局污染。

用途

例子2:闭包,嵌套函数闭包(最常用的创建闭包方式)

  1. function sayHi() {
  2. let name1 = "John";
  3. return function(){
  4. console.log(name1)
  5. }
  6. }
  7. let fun = sayHi()
  8. fun() //打印出“John”

该解决办法是把内层函数作为外层函数的返回值!

闭包的缺点是什么?

看例子2,函数 sayHi 放在变量 fun 里面,执行 sayHi 后,只要变量 fun 一直存在,那么函数 sayHi 就不会得到释放,也就不会被浏览器垃圾回收,一直占用内存。

如果这个函数里面的变量不多,那么占用的内存可有可无,但是如果是DOM操作呢?

这种现象,就叫做内存泄漏,也叫内存溢出,闭包的一个缺点。

:::info 补充:
虽然闭包并不会造成内存泄露,真实原因是 JS 引擎的实现有问题,但网上已经以讹传讹了。所以面试时我们依然要答出这一点。 :::

【资料来源】 JS完美收官之——闭包 JavaScript.info—闭包

你真的理解闭包和lambda表达式吗 什么是闭包?