Javascript 闭包: 通过一个例子了解 let 和 var 的区别

7

尽管我知道let允许您声明仅限于块作用域的变量,但在使用它与JavaScript闭包时,我遇到了一个奇怪的letvar之间的区别。 这是我的例子:

使用let

function buildFunction() {

  var arr = [];
  for(var i = 0; i < 3; i++) {
    let j = i;  //Using let to assign j
    arr.push(
      function(){
        console.log(j);
      }
    )
  }
   return arr;
}

var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();

上面的代码片段输出:
0
1
2

使用 var

function buildFunction() {

  var arr = [];
  for(var i = 0; i < 3; i++) {
    var j = i;  //Using var to assign j
    arr.push(
      function(){
        console.log(j);
      }
    )
  }
   return arr;
}

var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();

上面的代码片段输出如下内容:
2
2
2

我的问题是:

  1. 如果我在块内使用 var 并在执行期间给它赋值,那么它不应该像 let 一样在内存中存储不同的副本吗?

  2. Javascript 在闭包中是否以不同方式处理 letvar

非常感谢任何关于此的澄清。


@oen44:不,这不是重复的。请仔细阅读问题。 - g.005
我认为它完美地回答了“如果我在块内使用var,它不应该像let一样工作吗?”的问题。那么第二个问题的答案(否)就变得无关紧要了。 - Bergi
1个回答

12
var关键字限定变量的作用域在函数内部;let关键字会将变量的作用域限制在代码块中。
举个例子,当你使用var时,变量j的作用域被限制在buildFunction()函数内部。这意味着你在每个函数中都在使用“相同”的j。当for循环运行时,j被设置为0,然后是1,最后是2。当你运行控制台日志时,你在引用被设置为2的j,所以你得到了“2 2 2”。
当你使用let时,你将j的作用域限制在for循环的迭代中。这意味着每次迭代都有一个“不同”的j,并且控制台日志打印出你期望的结果。
如果你正在使用ES5并需要使用var,你可以通过将原始匿名函数封装到自执行函数中并将j作为参数传递来复制let效果(使其打印“0 1 2”)。这将为自执行函数创建一个新的作用域,该作用域的值是当前迭代中j的值。

function buildFunction() {

  var arr = [];
  for(var i = 0; i < 3; i++) {
    var j = i;  //Using var to assign j
    arr.push(
      //self executing function
      (function(j) { //j here is scoped to the self executing function and has the value of j when it was called in the loop. Any changes to j here will not affect the j scope outside this function, and any changes to j outside this function will not affect the j scoped inside this function.
        //original function
        return function() {
          console.log(j);
        }
      })(j) //call with the value of j in this iteration
    )
  }

  return arr;
}

var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接