闭包的定义
- 闭包是一个函数和其声明词法作用域的结合 --MDN
-
关于闭包的解释和理解各不相同在此引用MDN给出的定义作为参考
词法作用域
- 函数在执行的过程中,先从自己内部找变量
- 如果找不到,再从创建当前函数所在的作用域(词法作用域)去找, 以此往上
- 注意找的是变量的当前的状态
函数连同它作用域链上的要找的这个变量,共同构成闭包
简单点,闭包就是一个变量,这个变量可以临时存储数据,一般来说和一个函数同时存在.
- 一般情况下使用闭包主要是为了
- 封装数据
-
暂存数据
文字叙述太抽象了,下面我们来看一个典型的闭包案例.
一个典型的闭包案例
function car() {
var speed = 0
function fn() {
speed++
console.log(speed)
}
return fn
}
var speedUp = car()
speedUp() //1
speedUp() //2
-
一般来说,在car执行完成后内部的局部变量就会被销毁, speed将不复存在, 因为引擎启用了垃圾回收器,在内存不再被使用时来回收它们。但是闭包不会让这一切发生。在car里面再声明一个函数赋值给speedUp. 全局作用域是一直存在的, speedUp也就一直存在, 那么fn也就一直存在, fn在使用car内部的speed所以speed也不会被销毁. 当我们执行speedUp就是在执行fn,fn内部没有speed就会从上一级的词法作用域去找, 也就是car内部的speed. 这就导致闭包出现.
那么这么好的东西到底有什么用呢??
-
当我们调用speedUp这个函数的时候,可以把speed加1,这个变量就被暂存下来,我们无法直接访问这个变量,但是我们可以操作它,并且不会被销毁,speed封装成功.
这就是一个典型的闭包
闭包相关案例
这是一个常见的笔试题
如下代码输出多少?如果想输出3,那如何改造代码?
var fnArr = [];
for (var i = 0; i < 10; i++) {
fnArr[i] = function () {
return i
};
}
console.log(fnArr[3]()) // 10
为什么输出10?
- 我函们在做遍历时就赋值了, 但数内部并没有执行执行, 我们执行fnArr[3]时就等于执行fnArr[i]然后return i, 此时函数内部没有i, 它会从创建这个函数的词法作用域去找这个i,因为for循环并不是一个函数所以它的词法作用域就是全局作用域, 全局作用域中for循环完成后,i变成10, 他找到了10输出.
如果想输出3,那如何改造代码?
var fnArr = []
for (var i = 0; i < 10; i++) {
fnArr[i] = (function (j) {
return function () {
return j
}
})(i)
}
console.log(fnArr[3]()) // 3
- 这样就是相当于生成了10个闭包存了不同的10个值,当执行对应函数的时候就会往上找自己对应的临时变量.