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

Why can't assignment be done before the variable declaration with let and const?

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

对于letconst,变量被提升但未初始化。

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

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

但为什么我们不能在实际声明之前为 name 赋值,即使 name 已经被声明(提升)?

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

上面的代码和下面的是不是一样,

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

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

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

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

下面是一个具体示例,说明 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 在范围内。但是有,因为foo最下面的let吊了。赋值时出错,因为内部 a 未初始化。

相比之下,var没有错误,但很容易混淆哪个a分配在foo的顶部。


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

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

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

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

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

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

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

这里 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. “初始化”绑定(使其可用,将 usable 设置为 true),并单独
  2. “正在初始化”,如设置绑定的初始值。

它们是不同的东西。