为什么不能在用let和const声明变量之前完成赋值?

我在多个网站(如 w3schools)上读到过,提升是“将所有声明移动到当前范围顶部的行为”。

对于letand const,变量被提升但没有初始化。

我理解为什么以下代码不起作用,因为name它对我们没有访问价值。

console.log(name);
let name = "hi";

回答

因为它们被明确设计为不允许这样做,因为这通常是编程错误。

let并且const 提升,但这只是提升的绑定的声明。(松散地,“绑定”的意思是“变量”[或常量或参数......我们用来保存值的名称的东西]。)绑定直到稍后在逐步到达or语句时才初始化代码的执行。您不能使用未初始化的绑定(以任何方式),这就是您收到错误的原因。letconst

相比之下,var声明初始化都被提升了;var绑定用值初始化undefined。如果var( var a = 42)上有初始化值,稍后在var代码的逐步执行中到达该语句时,该部分将被视为简单赋值 ( a = 42)。使用letand const,它不仅仅是简单的赋值,它是绑定的初始化,允许它被使用。

这是一个具体的例子,说明如何let提升声明而不是初始化,以及为什么它有助于防止编程错误:

let a = 1;

function foo() {
    a = 2;  // <=== Which `a` should be assigned to?
    console.log(a);
    
    // code
    // code
    // code
    // code
    // code
    // code
    // code
    // code

    let a = 3;
    console.log(a);
}

foo();

在该代码中,似乎顶部的foo分配应该分配给外部a,因为(据我们所知,自上而下阅读)没有其他a范围。但是有,因为let底部foo是吊起的。执行分配时会出错,因为该内部a未初始化。

相比之下,使用var,没有错误,但很容易混淆afoo.


在评论中,您表示您仍然不理解声明绑定但未初始化的绑定意味着什么。我认为“初始化”¹ 的两个(略微)含义在这里让您感到困惑(当我进入这些东西时他们让我感到困惑),所以让我们稍微改变术语。

绑定有一个与之关联的标志,说明它们是否可以使用。让我们称它为usable标志:usable = true表示可以使用绑定,usable = false表示不能使用。使用该术语,上面的示例是这样处理的:

  1. 创建脚本的执行上下文时:

    1. 为其中的所有顶级声明创建绑定:
      • let a部分let a = 1;创建一个绑定调用a,其usable标志设置为false(尚不能使用)。
      • 函数声明 ( function foo() { }) 创建一个被调用的绑定foo,其usable标志设置为true(可以使用)并且其值设置为undefined
    2. 上下文中的函数声明是通过创建它们定义的函数并将它们分配给绑定来处理的。所以foo得到它的函数值。
  2. 当在let a = 1;代码的逐步执行中遇到语句时,它会做两件事:将usable标志设置为true(可以使用)并将值设置a1

  3. foo被调用并创建调用的执行上下文时,会创建顶级声明的绑定:

    1. 调用的绑定alet a = 3;由其usable标志设置为false(尚不能使用)创建的。
  4. 当在a = 2;代码的逐步执行中到达该语句时,将a解析为内部a绑定(在 中foo,由 声明的let a = 3;那个),但该绑定的usable标志是false,因此尝试使用它会引发错误。

  5. 如果我们没有a = 2;语句,所以没有抛出错误,那么当一步一步的代码执行到达let a = 3;语句时,它会做两件事:将usable标志设置为true(可以使用)并将值设置a3.

这里foo更新了一些评论:

function foo() {
    // The local `a` is created but marked `usable` = `false`

    a = 2;      // <=== Throws error because `a`'s `usable` is `false`
    console.log(a);
    
    let a = 3;  // <=== If there weren't an error above, this would set
                //      `usable` to `true` and the value of `a` to `3`
    console.log(a);
}

¹ “我认为“初始化”的两个(稍微)含义¹在这里让您感到困惑......”我所指的两个含义是:

  1. “初始化”绑定(使其可用,设置usabletrue),并单独
  2. “初始化”就像设置绑定的初始值一样。

它们是分开的东西。


以上是为什么不能在用let和const声明变量之前完成赋值?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>