Javascript的闭包应该了解到什么程度?

68 阅读3分钟

小目标

  • 能够认出什么是闭包
  • 能够会验证
  • 能够写代码的时候,灵活设计运用闭包

定义

首先闭包正确的定义是:假如一个函数能访问外部的变量,那么就形成了一个闭包,而不是一定要返回一个函数。这个定义很重要,下面的内容需要用到。

应用场景

  • 将某一函数中的数据隐藏,通过使用api提供给外界访问
  • 可以读取外层函数内部的变量,并让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在

作用

  • 延长外部函数的变量对象的生命周期。
  • 在函数外部的全局作用域,可以访问函数内部的局部变量,让数据变的更安全。
  • 延伸了变量的作用范围。

触发方式

  • 函数作为参数被传递
  • 函数作为返回值被返回

高阶函数是闭包吗

image.png

代码演示

let a = 1
// 产生闭包
function fn() {
  console.log(a);
}

function fn1() {
  let a = 1
  // 产生闭包
  return () => {
    console.log(a);
  }
}
const fn2 = fn1()
fn2()

进一步剖析

通过 Chrome 的开发工具来进一步了解闭包是如何存储外部变量的。先来说下数据存放的正确规则是:局部、占用空间确定的数据,一般会存放在栈中,否则就在堆中(也有例外)。 那么接下来我们来验证这个说法:

image.png

图中画红框的位置我们能看到一个内部的对象 [[Scopes]],这个对象就是我们常说的作用域链。根据作用域链寻找的顺序,其中包含了闭包、全局对象。据此我们能通过闭包访问到本该销毁的变量,所以我会在上一章节中说原始数据一般存放在栈上。

再度剖析

另外最开始我们对于闭包的定位是:假如一个函数能访问外部的变量,那么就形成了一个闭包,因此接下来我们看看在全局下的表现是怎么样的。

let a = 1
var b = 2
// 形成闭包
function fn() {
  console.log(a, b);
}

image.png

从上图我们能发现全局下声明的变量,如果是 var 的话就直接被挂到 global 上,如果是其他关键字声明的话就被挂到 Script 上。虽然这些数据同样还是存在 [[Scopes]] 上,但是全局变量在内存中是存放在静态区域的,因为全局变量无需进行垃圾回收。

最后总结一下原始类型存储位置:局部变量被存储在栈上,全局变量存储在静态区域上,其它都存储在堆上。

总结

闭包能考的很多,概念和笔试题都会考。

概念题就是考考闭包是什么了。

笔试题的话基本都会结合上异步,比如最常见的:

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

这道题会问输出什么,有哪几种方式可以得到想要的答案?

练习部分,打算单独整一篇去介绍。

参考