JavaScript中的闭包

在讲闭包之前,需要先了解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 ,它捕获的valueoutdated(过期的),也就是0,所以无论increment()执行多少次,打印出来的都是0

如何fixed这个问题?

function log() {
	const message = `Current value is ${value}`;
  console.log(message)
}
复制代码

如上所示,在每次调用log()时,重新获取value的值即可

stale closure: 就是closure捕获的是一个outdated的值

参考

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享