关于 js 闭包(stale closure)问题的疑惑?

网上看到如下两段代码:

function createIncrement(i) {
  let value = 0;
  function increment() {
    value += i;
    console.log(value);
    const message = `Current value is ${value}`;
    return function logValue() {
      console.log(message);
    };
  }
  
  return increment;
}

const inc = createIncrement(1);
const log = inc(); // 1
inc();             // 2
inc();             // 3
// Does not work!
log();             // "Current value is 1"
function createIncrementFixed(i) {
  let value = 0;
  function increment() {
    value += i;
    console.log(value);
    return function logValue() {
      const message = `Current value is ${value}`;
      console.log(message);
    };
  }
  
  return increment;
}

const inc = createIncrementFixed(1);
const log = inc(); // 1
inc();             // 2
inc();             // 3
// Works!
log();             // "Current value is 3"

第二段代码与第一段代码的唯一区别仅仅在于, 将message变量移入了 logValue()函数中, 似乎就能"正确"读取到value的值

两端代码均存在嵌套的闭包, 我个人分析的时候比较困惑, 个人理解为: value由于inc()的调用应该是不断变化的, 而由于存在最顶端的createIncrement()/createIncrementFixed()这个函数造成的闭包, 所读取的 value 值应该总是最新的即为 3? 但是似乎和实际结果有些不太符合, 不知道哪里理解错了, 恳请知道的前辈能详细分析一下? 不胜感激!

阅读 288
评论
    1 个回答

    从下面几个部分分析:

    第一个误区:

    1. value 这个的变量的声明 被执行了几次? 只有一次对吧
    2. message 这个变量的声明,被执行了几次? 是不是 inc() 函数被执行几次就会声明几次

    因此value 只被声明了一次,而且是在 局部作用域(闭包中),所以后续的访问都是同一个地方

    message虽然也在局部作用域中,但是每次都被重新声明(而不是重新赋值);所以语法上看是 logValue 里面的 使用的是同一个,但是其实内存中不是同一个;因此 第一种写法,log 执行但结果都是当时 inc()执行时的值

    第二个误区:

    在第二种写法中不仅仅是代码的位置发生了变化,同时值的引用也发生了变化; 当你任何时候执行 log 时,他都是访问 value 的值而和 message 没关系了,然而我们之前说了 value 会不断被重新赋值,因此 log 每次执行都是拿到最新的。

    第三 换个写法实现,让message 也只声明一次,代码如下

    function createIncrement(i) {
      let message;
      let value = 0;
      function increment() {
        value += i;
        console.log(value);
        message = `Current value is ${value}`;
        return function logValue() {
          console.log(message);
        };
      }
      return increment;
    }

    这个时候 任何时候执行 log 也是一样 拿到最新的值

    希望对你有帮助

      撰写回答

      登录后参与交流、获取后续更新提醒