907 字
5 分钟
Js中的闭包
什么是闭包?为什么要用闭包?闭包的工作方式?闭包的常见应用?
什么是闭包
:::tip 闭包的概念
函数和对其词法环境lexical environment
的引用捆绑在一起构成闭包
也就是说,闭包可以让内部函数访问外部函数的作用域。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
:::
最简单的例子
function compare(propertyName) {
function(obj1,obj2){
let value1 = obj1[propertyName]
let value2 = obj2[propertyName]
return (value1 - value2 < 0)
}
}
TIP 在这个例子中,内部的匿名函数中的变量
value1和value2
,引用了外部函数的propertyName
变量
闭包的特点
:::tip 特点
- 函数嵌套函数
- 函数内部可以引用函数外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
:::
闭包的一些应用
1. 使用闭包模拟私有方法
TIP 使用闭包来定义公共函数,并令其可以访问私有函数和变量,这种方式也叫模块方式
两个计数器
counter1和counter2
是相互独立的 其它例如计数器、延迟调用、回调等闭包的应用,其核心思想还是创建私有变量和延长变量的生命周期
const Counter = function () {
let privateCounter = 0
function changeBy(val) {
privateCounter += val
}
return {
increment: function () {
changeBy(1)
},
decrement: function () {
changeBy(-1)
},
value: function () {
return privateCounter
}
}
}
const counter1 = new Counter()
const counter2 = new Counter()
counter1.increment()
counter1.increment()
console.log(counter1.value()); // 2
console.log(counter2.value()); // 0
// ...
2. 函数柯里化
TIP 柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能够轻松的重用
function getArea(width, height) {
return width * height
}
const area1 = getArea(10, 20)
const area2 = getArea(10, 30)
const area3 = getArea(10, 40)
// 如果我们碰到的长方形的宽老是10,我们可以使用闭包柯里化这个函数
function getArea(width) {
return (height) => {
return width * height
}
}
const getTenWidthArea = getArea(10)
// 之后碰到宽度为10的长方形就可以这样计算面积
const area = getTenWidthArea(20)
// 而且如果遇到宽度偶尔变化也可以轻松复用
const getTwentyWidthArea = getArea(20)
3. 定时器和闭包
for(var i = 0;i<5;i++){
setTimeout(()=> {
console.log(i)
},100)
}
// 结果是输出 5 次 5
WARNING 先来看一个例子,这个例子大家应该都知道为什么
var
只有函数作用域,而{}
相当于形成了一个块作用域,所以5次循环用的是同一个i,再加上for
循环内部的seTimeout
还是一个异步任务,所以在执行seTimeout
的回调之前,i
就先自增到了5解决:
- 最简单的解决办法可以将
var
改为ES6中的let
- 引入闭包来保存变量(立即执行函数)
for(var i = 0;i<5;i++){
(function(i) {
setTimeout(() => {
console.log(i)
},100)
})(i)
}
// 0 1 2 3 4
如果想每隔100毫秒依次输出,而不是同时:
for (var k = 0; k < 5; k++) {
(function(k) {
setTimeout(() => {
console.log(k)
}, 100 * k)
})(k)
}
闭包的使用场景
TIP
- 创建私有变量
- 延长变量的生命周期
闭包的总结
:::tip 优点
- 可以访问到函数内部的局部变量
- 可以避免全局变量的污染
- 这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。
:::
:::danger 缺点
会增大内存使用量,滥用闭包会影响性能,导致内存泄漏等问题。
在退出函数之前,将不使用的局部变量全部删除,可以使变量赋值为null,这样就会被回收.
:::