公司网站设计的企业,宜春建设局网站,网站常用的颜色,广州优秀网站建设目录 1#xff0c;什么是闭包2#xff0c;创建闭包3#xff0c;如何销毁闭包2.1#xff0c;自动创建的闭包2.2#xff0c;手动创建的闭包 4#xff0c;闭包的特点和使用场景3.1#xff0c;特点3.2#xff0c;使用场景避免全局变量污染函数柯里化 5#xff0c;闭包经典… 目录 1什么是闭包2创建闭包3如何销毁闭包2.1自动创建的闭包2.2手动创建的闭包 4闭包的特点和使用场景3.1特点3.2使用场景避免全局变量污染函数柯里化 5闭包经典问题 闭包的定义有许多的说法下面来介绍下我理解的。
1什么是闭包
首先在 js 中闭包是通过作用域链来实现的。
闭包可以看做是一个封闭的空间用来存储当前作用域的变量来在其他地方引用。
2创建闭包
执行函数时只要在函数中使用了外部数据就会创建闭包。
验证一下
function fun() {const i 1console.log(i) // 断点
}
fun()const i 1
function fun() {console.log(i)
}
fun()而变量是否放入到闭包中要看其他地方有没有对这个变量的引用。
举例
const a 1function fun() {const b 2const c 3function fun1() {const d 4const e 5function fun2() {console.log(a, b, c, d)}fun2()}fun1()
}
fun()可以看到创建了3个闭包分别存储对应作用域的变量。
全局funfun1
而变量 e 并没有被放到闭包中因为没有被引用。
3如何销毁闭包
闭包的产生会占用空间。那如何销毁闭包释放空间
创建闭包分为2种情况销毁也有所区别。
自动创建的闭包手动创建的闭包
2.1自动创建的闭包
自动创建的闭包在函数调用完会直接销毁掉。
在上面的例子中fun() 执行完成后变量 a 在全局环境中没有在全局闭包中正常输出变量b 会报错。
fun()
console.log(a) // 1
console.log(b) // Error可以看到已经没有任何闭包存在了垃圾回收器会自动回收没有引用的变量 b,c,d,e不会有内存被占用的情况。
2.2手动创建的闭包
手动创建的闭包可以设置在函数调用完依然保留。
先看一个例子
function getUser() {const name 下雪天的夏风console.log(name);
}
getUser()
console.log(name); // Error很明显局部变量 name 会随着 getUser() 的执行上下文创建而创建销毁而销毁。所以getUser()执行完后name 也就不存在了打印报错。
修改代码如下
function getUser() {const name 下雪天的夏风return function () {console.log(name)}
}
const user getUser()
user() // 下雪天的夏风调用 user() 为什么能访问到 name因为垃圾回收器只会回收没有被引用的变量。原本getUser()调用完name就会被销毁掉但此时向外部返回了一个匿名函数该函数引用了 name所以name不会被垃圾回收器回收。
4闭包的特点和使用场景
3.1特点
通过闭包可以让外部环境访问到函数内部的局部变量。通过闭包可以暂存局部变量不随着它的上下文环境一起销毁。
因为这2个特点也就产生了下面的使用场景
3.2使用场景
避免全局变量污染
在 js 还无法模块化的时期多人协作有可能会导致定义的全局变量产生命名冲突。使用闭包可以将变量和对应的功能放到一个独立的空间中来在一定程度上解决全局变量污染问题。
const name 全局 // 全局变量const init (function () {const name 局部name1function callName() {console.log(name)}return function () {callName()}
})()
init() // 局部 name1const initSuper (function () {const name 局部name2function callName() {console.log(name)}return function () {callName()}
})()
initSuper() // 局部 name2函数柯里化
参考 这篇文章
5闭包经典问题
for (var i 0; i 3; i) {setTimeout(function () {console.log(i)}, 1000)
}上面的代码中我们预期 1s 后输出 0,1,2但实际会输出3个3。
这个问题就是因为闭包导致的。setTimeout 的回调函数访问了外部变量 i形成闭包。而变量 i 只有1个循环结束后访问的变量 i 也是同一个。
解决
方式1利用立即执行函数实现 setTimeout 的回调函数不再访问外部变量。
for (var i 0; i 3; i) {;(function (index) {setTimeout(function () {console.log(index)}, 1000)})(i)
}方式2利用 setTimeout 的第3个参数实现 setTimeout 的回调函数不再访问外部变量。
for (var i 0; i 3; i) {setTimeout(function (index) {console.log(index)},1000,i)
}// 或
for (var i 0; i 3; i) {setTimeout(console.log, 1000, i)
}方式3利用 let 关键字产生的块级作用域让每次循环都是新的变量i。
for (let i 0; i 3; i) {setTimeout(function () {console.log(i)}, 1000)
}注意是块级作用域的原因此时并没有产生闭包。 以上。