问题描述
我在多个网站(如 w3schools)上读到,提升是“将所有声明移动到当前范围顶部的行为”。
对于 let
和 const
,变量被提升但未初始化。
我理解为什么以下代码不起作用,因为 name
没有可供我们访问的价值。
console.log(name);
let name = "hi";
但是为什么我们不能在实际声明之前为 name
赋值,即使 name
已经声明(提升)?
name = "hi";
let name;
console.log(name);
上面的代码是不是和下面的一样,
let name;
name = "hi";
console.log(name);
解决方法
因为它们被明确设计为不允许这样做,因为这通常是编程错误。
let
和 const
是 提升了,但这只是提升了绑定的声明。 (松散地,“绑定”的意思是“变量”[或常量或参数......我们用来保存值的名称的东西]。)直到稍后,当 {{1} } 或 let
语句在代码的逐步执行中达到。您不能使用未初始化的绑定(以任何方式),这就是您收到错误的原因。
相比之下,使用 const
声明 和 初始化都被提升; var
绑定使用值 var
进行初始化。如果 undefined
(var
) 上有初始化值,稍后在代码的逐步执行中到达 var a = 42
语句时,该部分被视为简单赋值(var
)。使用 a = 42
和 let
,这不仅仅是简单的赋值,而是绑定的初始化,允许使用。
下面是一个具体的例子,说明 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
,因为(据我们所知,自上而下阅读)没有其他 {{1 }} 在适用范围。但是有,因为 a
底部的 a
被提升了。由于内部 let
未初始化,因此您在执行分配时出错。
相比之下,foo
没有错误,但很容易混淆 a
被分配在 var
的顶部。
在评论中,您表示您仍然不理解声明绑定但未初始化的绑定意味着什么。我认为“初始化”¹ 的两个(略微)含义在这里让您感到困惑(当我进入这些东西时它们让我感到困惑),所以让我们稍微改变术语。
绑定有一个与它们相关联的标志,说明它们是否可以使用。让我们称其为 a
标志:foo
表示可以使用绑定,usable
表示不能使用。使用该术语,上述示例的处理方式如下:
-
创建脚本的执行上下文时:
- 为其中的所有顶级声明创建绑定:
-
usable = true
的usable = false
部分创建了一个名为let a
的绑定,其let a = 1;
标志设置为a
(尚不能使用)。 - 函数声明 (
usable
) 创建了一个名为false
的绑定,其function foo() { }
标志设置为foo
(可以使用),其值设置为 {{1 }}。
-
- 上下文中的函数声明是通过创建它们定义的函数并将它们分配给绑定来处理的。所以
usable
得到它的函数值。
- 为其中的所有顶级声明创建绑定:
-
在代码的分步执行中遇到
true
语句时,它会做两件事:将undefined
标志设置为foo
(可以使用)并将let a = 1;
的值设置为usable
。 -
当调用
true
并创建调用的执行上下文时,会创建顶级声明的绑定:- 名为
a
的绑定由1
创建,其foo
标志设置为a
(尚不能使用)。
- 名为
-
在代码的逐步执行中到达
let a = 3;
语句时,usable
解析为内部false
绑定({{ 1}},由a = 2;
声明),但该绑定的a
标志是a
,因此尝试使用它会引发错误。 -
如果我们没有
foo
语句,所以没有抛出错误,那么当分步代码执行时到达let a = 3;
语句,它会做两件事:将usable
标志设置为false
(可以使用)并将a = 2;
的值设置为let a = 3;
.
usable
更新了一些评论:
true
¹ “我认为“初始化”¹的两个(稍微)含义在这里让您感到困惑......”我所指的两个含义是:
- “初始化”绑定(使其可用,将
a
设置为3
),并单独 - “初始化”就像设置绑定的初始值一样。
它们是不同的东西。