在讲闭包之前,需要先了解scope(作用域)和lexical scope(词法作用域),具体可查看Scope、Lexical Scope、Scope Chain、Lexical Environment、Execution Context
闭包(Closure)
lexical scope 允许我们静态访问outer scope(外部作用域)中的变量
function outerFunc() {
// --- start lexical scope
let outerVar = 'I am outside!';
// start closure
function innerFunc() {
console.log(outerVar); // => logs "I am outside!"
}
// end closure
return innerFunc;
// --- end lexical scope
}
const myInnerFunc = outerFunc();
myInnerFunc();
复制代码
如上代码:myInnerFunc() 仍会打印出'I am outside!'
,这里有一些重要的点:
- 虽然innerFunc在它的lexical作用域之外被执行,但是其仍可以访问到outer scope中的变量outerVar
- 换句话说,innerFunc()从它的词法作用域上捕获了outerFunc()中定义的变量outerVar(这是闭包的一个重要特点)
总结:
闭包是一个函数,它能够访问到它的词法作用域,即使该函数在它的词法作用域之外被执行。换句话说,就是:闭包是一个函数,它可以从变量定义的位置记住变量(或者说捕获变量),即使该函数在它的词法作用域之外执行
闭包的应用场景
Event handler
let countClicked = 0;
myButton.addEventListener('click', function handleClick() {
countClicked++;
myText.innerText = `You clicked ${countClicked} times`;
});
复制代码
Callback
例1:
const message = 'Hello, World!';
setTimeout(function callback() {
console.log(message); // logs "Hello, World!"
}, 1000);
复制代码
例2:
let countEven = 0;
const items = [1, 5, 100, 10];
items.forEach(function iterator(number) {
if (number % 2 === 0) {
countEven++;
}
});
countEven; // => 2
复制代码
Functional Programming
function multiply(a) {
return function executeMultiply(b) {
return a * b;
}
}
const double = multiply(2);
double(3); // => 6
double(5); // => 10
const triple = multiply(3);
triple(4); // => 12
复制代码
stale closure
function createIncrement(incBy) {
let value = 0;
function increment() {
value += incBy;
console.log(value);
}
const message = `Current value is ${value}`;
function log() {
console.log(message);
}
return [increment, log];
}
const [increment, log] = createIncrement(1);
increment(); // logs 1
increment(); // logs 2
increment(); // logs 3
// Does not work!
log(); // logs "Current value is 0"
复制代码
log()
执行时打印的是Current value is 0
,但是此时value已经increment了3次——value = 3
,为什么log()
打印的却是0?
因为log()
是一个stale closure
,它捕获的value
是outdated
(过期的),也就是0,所以无论increment()
执行多少次,打印出来的都是0
如何fixed这个问题?
function log() {
const message = `Current value is ${value}`;
console.log(message)
}
复制代码
如上所示,在每次调用log()
时,重新获取value的值即可
stale closure: 就是closure捕获的是一个outdated的值
参考
- simple-explanation-of-javascript-closures/
- react hooks stale closures
- 《你不知道的JavaScript》上卷
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END