Blog
Blog copied to clipboard
作用域与闭包 - 闭包的实现原理和作用
闭包的实现原理和作用
闭包是什么
MDN对闭包描述如下:
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
闭包是函数和创建函数的环境的组合。
由 标准 可知,JavaScript中函数被创建时都会记录当前词法环境,所以说JavaScript中,每次创建函数,都会生成闭包。
闭包的作用
从技术层面说,静态作用域(词法作用域)是通过闭包实现的。
- 函数被创建时会记录所处的词法环境;
- 函数被调用时会创建新的词法环境(其中包含一个队外部词法环境的引用),并将外部词法环境引用指向函数创建时所记录的词法环境;
- 函数内使用自由变量(不在函数内定义的变量)时,沿着由词法环境组成的作用域链寻找解析。
这样就实现了静态作用域(词法作用域),也就是在编写代码时就能确定变量的解析过程。
我们常说的闭包是什么
我们常说的闭包,也可以说是“有意义”的闭包,具备以下两点特征:
- 函数创建时所在的上下文销毁后,该函数仍然存在;
- 函数内引用自由变量。
最常见的闭包就是父函数内返回一个函数,返回函数内引用了父函数内变量:
const scope = 'outer'
const genClosure = () => {
const scope = 'local'
return () => {
console.log(scope)
}
}
const closure = genClosure()
closure()
// <- 'local'
闭包的应用
任何关于闭包的应用总结起来都离不开以下两点:
- 创建私有变量,隐藏实现细节
- 延长变量声明周期
函数柯里化和偏函数
柯里化:把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数。
// 柯里化前
const getVolume = (l, w, h) => l * w * h
const volume1 = getVolume(100, 200, 100)
const volume2 = getVolume(100, 200, 300)
// 柯里化后
const getVolume = l => w => h => l * w * h
const getVolumeWithDefaultLW = getVolume(100)(200)
const volume1 = getVolumeWithDefaultLW(100)
const volume2 = getVolumeWithDefaultLW(300)
模块化
用于将内部实现封装,仅对外暴露接口,常见于工具库的开发中。
var counter = (function() {
var privateCounter = 0
function changeBy(val) {
privateCounter += val
}
return {
increment: function() {
changeBy(1)
},
decrement: function() {
changeBy(-1)
},
value: function() {
return privateCounter
}
}
})()
模拟块级作用域
最典型的就是ES6之前for循环中使用定时器延迟打印的问题。
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i)
}, i * 1000)
}
// <- 4
// <- 4
// <- 4
使用立即执行函数,将i作为参数传入,可保存变量i的实时值。
for(var i = 1; i <= 3; i++){
(i => {
setTimeout(() => {
console.log(i)
}, i * 1000)
})(i)
}
// <- 1
// <- 2
// <- 3