这是我参与「第四届青训营」笔记创作活动的第 13 天
闭包是什么
一个私有作用域,返回一个函数,作用域外只能通过函数操作私有作用域中的变量。
-
使用场景:需要私有变量,防止污染全局变量
-
缺陷:不及时回收闭包内部变量,容易引起内存泄漏
function Foo() {
var i = 0
return function() {
console.log(i++)
}
}
var f1 = Foo(),
f2 = Foo();
f1(); // 0
f1(); // 1
f2(); // 0
首先,Foo 是一个闭包,最直观的判断就是它返回的是一个函数,这个函数可以访问外层函数内的变量。
在上述例子中,i 是 Foo 函数内的局部变量,其他环境是无法访问的(比如 window 或在其他函数内),相反,Foo 内的函数却可以访问外部变量
另外,函数赋值的时候不会执行,也不会开辟新的内存,f1 拿到的只是地址而已,Foo 函数还没有使用,函数内部的变量也没有占用内存,Foo 只是一个字符串而已(以下是大佬的实验)
不需要闭包时,需要将闭包内的变量设为 null
下面看一个小小的🌰
我们期望打印 0 至 5,结果却是 5 个 5
function A() {
var arr = []
// var i = 233 但 i 赋值加在这里的话,结果还是 5 个 5
for (var i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i)
}
}
// var i = 233 如果这里加一行的话,结果会变成 5 个 233
return arr
}
var myArr = A()
for (var j = 0; j < 5; j++) {
myArr[j]() // 5 个 5
}
形成这个结果的原因主要有两个:
- for 循环执行时所建立的是块级作用域
{},块级作用域中使用var定义的变量还是属于该作用域所在的函数作用域,也就是logFun - 函数的作用域是静态作用域,也就是定义的时候就确定了,函数调用的时候会函数定义时形成的作用域链中查找所需参数,for 所在的块级作用域
logFun中i == 5,所以会打印 5 个 5
解决方法 1:使用立即执行函数
function logFunc() {
var arr = []
for (var i = 0; i < 5; i++) {
(function(j){
arr[j] = function () {
console.log(j)
}
}(i))
}
return arr
}
var myArr = logFunc()
for (var j = 0; j < 10; j++) {
myArr[j]()
}
解决方法 2:将 for 循环里的 var 改为 let
使它成为 for 块级作用域中的私有变量