为啥不能在用 let 和 const 声明变量之前进行赋值? [复制]

Posted

技术标签:

【中文标题】为啥不能在用 let 和 const 声明变量之前进行赋值? [复制]【英文标题】:Why can't assignment be done before the variable declaration with let and const? [duplicate]为什么不能在用 let 和 const 声明变量之前进行赋值? [复制] 【发布时间】:2021-09-15 11:21:21 【问题描述】:

我在多个网站(如 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);

【问题讨论】:

另见Are variables declared with let or const hoisted? 【参考方案1】:

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

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 的顶部。


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

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

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

      为其中的所有***声明创建绑定: let a = 1;let a 部分创建了一个名为a 的绑定,其usable 标志设置为false(尚不能使用)。 函数声明 (function foo() ) 创建一个名为 foo 的绑定,其 usable 标志设置为 true(可以使用),其值设置为 undefined。 通过创建它们定义的函数并将它们分配给绑定来处理上下文中的函数声明。所以foo 得到了它的函数值。

    当在代码的逐步执行过程中遇到let a = 1;语句时,它会做两件事:将usable标志设置为true(可以使用)并设置@的值987654363@转1

    当调用foo 并创建调用的执行上下文时,会创建***声明的绑定:

      名为a 的绑定由let a = 3; 创建,其usable 标志设置为false(尚不能使用)。

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

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

这里是 foo 更新了一些 cmets:

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);


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

    “初始化”绑定(使其可用,将usable 设置为true),并单独进行 “初始化”就像设置绑定的初始值一样。

它们是不同的东西。

【讨论】:

好的。有什么好的资源可以阅读吗? @TusharShahi - 冒着自我推销的风险,我最近的一本书 javascript: The New Toys 的第 2 章详细介绍了这一点;链接here。 ;-) 如果let a = 3的声明被提升到函数的顶部,为什么程序不只是输出2后跟3? @NewbieToCoding - 因为,正如我在回答中所说,绑定的 initialization 没有被提升,你不能使用未初始化的绑定。 但是let a; a = 3;a = 3; let a; 有什么不同,如果在第二种情况下,声明被提升以使其与第一个代码相同?为什么第二个代码会产生错误?

以上是关于为啥不能在用 let 和 const 声明变量之前进行赋值? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

ES6教程:let和const命令的用法

ES6 - let & const

var,let和const的区别是什么?

ES6之let和const的区别

深入理解ES6 - var-let-const

面试知识-es6