ChuChencheng / note

菜鸡零碎知识笔记
Creative Commons Zero v1.0 Universal
3 stars 0 forks source link

let 与 var 的区别 #9

Open ChuChencheng opened 4 years ago

ChuChencheng commented 4 years ago

作用域规则

一个很经典的场景:

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

在 for 循环中,如果用 var 去定义 i ,输出的结果是一样的,而如果用的是 let ,输出则是符合直觉的 1, 2, 3

来具体分析一下,上面这段代码,假设不是在函数中执行的,就是打开浏览器控制台复制粘贴进去执行,所以是在 全局作用域 中执行的。

var i = 0 这个操作,是在 全局作用域 中定义的,因此 i 就是个全局变量了。 所以在执行函数的时候,函数内的作用域没找到 i ,因此到全局作用域中去找,所以输出的结果都是 i 最后被赋的值。而 i 因为是个全局变量,所以在 for 循环后依然能被访问到。

如果用的 let ,则 i 是被定义在 for 循环的这个块中,因此在执行函数的时候,在函数作用域中没找到,转而去上一层的块级作用域中找。而在 for 循环结束后, i 也没法被访问了,如果执行后试图输入 i ,会报 ReferenceError ,因为 i 并没有在全局作用域中定义。

变量提升

function run() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

上述代码中, foo 变量在函数中声明了,因为变量提升,在语句之前是一个声明了但未赋值的状态,所以第一个 console.log 输出 undefined

对应使用 let 的情况:

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

在初始化之前访问 let 声明的变量时,变量处于一个 暂时死区 ,即还没声明也没有赋值,因此会报 ReferenceError

全局对象属性

在全局作用域中, let 声明的变量不会被挂到全局对象上

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

重复声明

参考

Stack Overflow