877 字
4 分钟
Js中的闭包
什么是闭包
闭包的概念 函数和对其词法环境
lexical environment的引用捆绑在一起构成闭包 也就是说,闭包可以让内部函数访问外部函数的作用域。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
最简单的例子
function compare(propertyName) { function(obj1,obj2){ let value1 = obj1[propertyName] let value2 = obj2[propertyName]
return (value1 - value2 < 0) }}NOTE 在这个例子中,内部的匿名函数中的变量
value1和value2,引用了外部函数的propertyName变量
闭包的特点
特点
- 函数嵌套函数
- 函数内部可以引用函数外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
闭包的一些应用
1. 使用闭包模拟私有方法
NOTE 使用闭包来定义公共函数,并令其可以访问私有函数和变量,这种方式也叫模块方式
两个计数器
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()); // 2console.log(counter2.value()); // 0// ...2. 函数柯里化
NOTE 柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能够轻松的重用
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 次 5WARNING 先来看一个例子,这个例子大家应该都知道为什么
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)}闭包的使用场景
NOTE
- 创建私有变量
- 延长变量的生命周期
闭包的总结
优点
- 可以访问到函数内部的局部变量
- 可以避免全局变量的污染
- 这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。
缺点 会增大内存使用量,滥用闭包会影响性能,导致内存泄漏等问题。
在退出函数之前,将不使用的局部变量全部删除,可以使变量赋值为null,这样就会被回收.