文章

对 var、let、const 的一些研究:重复声明 | 变量提升 | 暂时性死区

【省流】三者的区别

(还没整理)

现象观察

在用 VS Code 尝试这三种变量的重复声明时,发现了一些很神奇的情况:

  • 这种情况下,执行前不会预报任何错误,but 执行后会报错:

    Identifier ‘a’ has already been declared.

    ——这里指的是 let 类型中,同一变量不能重复声明。

1
2
3
4
5
6
7
8
9
10
var a = 1;
var a = 2;

let a = 1;
let a = 2; 

// const a = 1;

let b = a;
console.log(a);
  • 但这种情况,IDE 就会给你报错:

“无法重新声明块范围变量“a”。”

—— typescript 语法检查的启用真是神奇(什么)

1
2
3
4
5
6
7
8
9
10
// var a = 1;
// var a = 2;

let a = 1;
let a = 2; 

// const a = 1;

let b = a;
console.log(a);
  • 这种情况,IDE 是能正常执行的:

最后会正常打印 “2” 的值。

1
2
3
4
5
6
7
8
9
10
var a = 1;
var a = 2;

// let a = 1;
// let a = 2; 

// const a = 1;

let b = a;
console.log(a);

现象解释:var VS let 的变量声明

它们都各自干了什么,导致一个会报错、另一个却不会报错?

  • 当使用 var 重复声明一个变量时,JavaScript 解释器会将其视为对同一个变量的重新绑定,而不是一个新的声明。
  • 如果新的声明没有提供初始化器,它实际上什么都不做,只是维持现有变量的值。
  • 如果新的声明提供了初始化器,那么该变量的值会更新为新初始化器提供的值。

变量提升 & 暂时性死区

道理很简单粗暴,但之前没看过的话就会一眼懵的那种。

或许我们还能从另一方面考虑,对于var声明的变量,它存在着变量提升,即无论你在何处用var声明变量,系统会自动将该变量的声明提升到当前作用域的顶部。

let声明的变量同样存在变量提升;但对于let声明的变量,则存在另一种特性:暂时性死区。let声明的变量会“绑定”当前作用域,如果在此let声明之前使用该变量,则会抛出错误,即形成暂时性死区(就是不能在声明之外/之前提前使用)。

(在 “从块作用域开始、到 let 变量声明,这块区域是 ‘禁区 / 死区’ ,不能使用该 let 变量”。)

作者:止曰链接:https://juejin.cn/post/7033001234709610509来源:稀土掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 所以可以利用 var 的变量提升,在??中先使用、后声明。

——但要注意的是,var 类型的变量提升提升的只有声明,而赋值是仍然滞后的。

比如这段代码,输出为 undefined:

1
2
console.log(myVar); // 输出:undefined
var myVar = 10;

let和const声明的变量也会提升,但它们不会被初始化,所以在提升后会处于一个所谓的暂时性死区(Temporal Dead Zone,TDZ),直到声明被实际执行。 如果在TDZ中访问这些变量,将会抛出一个引用错误。 ——let真的没有变量提升吗?let暂时性死区引发的思考 - CSDN博客

【补充】函数作用域 VS 块作用域

1. 具体区别

用 var 声明的变量的作用域是它当前的执行上下文,即如果是在任何函数外面,则是全局执行上下文,如果在函数里面,则是当前函数执行上下文。换句话说,var 声明的变量的作用域只能是全局或者整个函数块的。

2. 产生区别的原因

  • var 产生的历史背景:

    JavaScript 诞生时只有 10 天的设计周期,Brendan Eich 为了快速让 JS 能运行在浏览器里,借鉴了 C/Java 语法和 Scheme 的部分思想。

    所以你能感受到,“变量提升” 中的声明提前(在函数级别预先声明)这一点,和 C 特别像。

    ——然后就渐渐出现了 “历史遗留” 问题:

    • 随着项目规模增大,人们发现:
      • var 没有块级作用域,for 循环里声明的变量会泄露到外层。
      • 变量提升让代码 可读性差、容易出 bug
    • 于是开发者社区常用“闭包技巧”或 IIFE(立即执行函数表达式)模拟块级作用域。

      1
      2
      3
      4
      5
      6
      7
      
        (function() {
            var i;
            for (i = 0; i < 3; i++) {
                console.log(i);
            }
        })();
        console.log(i); // ReferenceError
      
本文由作者按照 CC BY 4.0 进行授权