小目标
- 能够认出什么是闭包
- 能够会验证
- 能够写代码的时候,灵活设计运用闭包
定义
首先闭包正确的定义是:假如一个函数能访问外部的变量,那么就形成了一个闭包,而不是一定要返回一个函数。这个定义很重要,下面的内容需要用到。
应用场景
- 将某一函数中的数据隐藏,通过使用api提供给外界访问
- 可以读取外层函数内部的变量,并让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在
作用
- 延长外部函数的变量对象的生命周期。
- 在函数外部的全局作用域,可以访问函数内部的局部变量,让数据变的更安全。
- 延伸了变量的作用范围。
触发方式
- 函数作为参数被传递
- 函数作为返回值被返回
高阶函数是闭包吗
代码演示
let a = 1
// 产生闭包
function fn() {
console.log(a);
}
function fn1() {
let a = 1
// 产生闭包
return () => {
console.log(a);
}
}
const fn2 = fn1()
fn2()
进一步剖析
通过 Chrome 的开发工具来进一步了解闭包是如何存储外部变量的。先来说下数据存放的正确规则是:局部、占用空间确定的数据,一般会存放在栈中,否则就在堆中(也有例外)。 那么接下来我们来验证这个说法:
图中画红框的位置我们能看到一个内部的对象 [[Scopes]],这个对象就是我们常说的作用域链。根据作用域链寻找的顺序,其中包含了闭包、全局对象。据此我们能通过闭包访问到本该销毁的变量,所以我会在上一章节中说原始数据一般存放在栈上。
再度剖析
另外最开始我们对于闭包的定位是:假如一个函数能访问外部的变量,那么就形成了一个闭包,因此接下来我们看看在全局下的表现是怎么样的。
let a = 1
var b = 2
// 形成闭包
function fn() {
console.log(a, b);
}
从上图我们能发现全局下声明的变量,如果是 var 的话就直接被挂到 global 上,如果是其他关键字声明的话就被挂到 Script 上。虽然这些数据同样还是存在 [[Scopes]] 上,但是全局变量在内存中是存放在静态区域的,因为全局变量无需进行垃圾回收。
最后总结一下原始类型存储位置:局部变量被存储在栈上,全局变量存储在静态区域上,其它都存储在堆上。
总结
闭包能考的很多,概念和笔试题都会考。
概念题就是考考闭包是什么了。
笔试题的话基本都会结合上异步,比如最常见的:
for (var i = 0; i < 6; i++) {
setTimeout(() => {
console.log(i)
})
}
这道题会问输出什么,有哪几种方式可以得到想要的答案?
练习部分,打算单独整一篇去介绍。
参考
- 掘金小册《Web 前端面试指南与高频考题解析》
- 掘金小册《前端面试之道》