变量声明是否与变量的绑定相同?

Is a variable declaration the same as a variable's binding?

MDN documentation 状态:

let bindings are created at the top of the (block) scope containing the declaration, commonly referred to as "hoisting". Unlike variables declared with var, which will start with the value undefined, let variables are not initialized until their definition is evaluated. Accessing the variable before the initialization results in a ReferenceError. The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.

let绑定”是指(letconst的提升)只是关键字let,还是只是创建存储space(与关键字无关)?

之前我以为变量关键字和变量名一起构成了一个声明,但是在最近的一个问中,回答者说它们实际上是一个初始化。

这里要理解的主要事情是 js 引擎实际上在两种不同的情况下访问 let 语句(以及所有其他语句,但它在这里特别重要)。它在解析期间被访问一次,当它生成 AST 并分析范围和变量时。它还为每个范围创建一个变量列表。现在,当代码被执行时,引擎会第二次访问该语句(如果它在循环/函数/其他内容中,则更频繁),现在最终初始化变量并为其赋值。所以"hoisting"基本上只是因为解析/执行阶段造成的,引擎在执行过程中在到达声明语句之前就知道变量存在,因为它之前已经解析过了。

Is the "let binding" referrred to (the hoisting of let and const) just the keyword let, or is it just the creation of storage space (which doesn't have to do with the keyword)?

该关键字实际上会在范围记录中产生一个条目,然后在执行期间将其转换为存储 space。另一方面,语句本身会在执行期间引起初始化。所以实际上很难说什么时候声明发生,这是一个措辞问题。俗话说

该变量是在第 10 行声明的

它在那个块中声明

所以 "declaration" 是指语句还是范围分配由您决定:)

the answerer said they are actually an initialization.

实际上回答者更喜欢称它为 "initialization" 而不是 "declaration" 以免混淆读者,但实际上它令人困惑,因为人类语言不像机器语言那样明确定义。

一个声明只是说某物存在。在 JavaScript 中,您可以声明变量、函数和(最近)类.

在某些语言(例如 C、C++)中,可以 声明 某些东西而无需 定义 它。例如:

// this declares a function exists with a given signature, but doesn't define its implementation
void someFunction();

someFunction(); // here we call the function, since we know it exists

// here we define the function, which we have to do at some point
void someFunction() { /* ... */ }

这种模式在现代语言中不太常见,在现代语言中,声明和定义往往结合在一起,但理解区别很有用,因为您的问题似乎主要与术语有关。

可以声明变量,但它们没有定义。

let b; // we declare that there's a variable 'b'

相反,您可以分配一个变量:

b = 5; // assignment
let c = 6; // declaration and assignment in one statement

计算机科学中绑定的概念有many forms。例如,当您在代码中键入 foo 时,绑定就是确定应该使用哪个 variable/function/type/... 的行为。在 JavaScript 中,这非常简单,但在某些语言中,它可能会变得非常复杂(由于重载解析等原因)。

但是我不相信这就是 MDN 在谈论 let 绑定 时的意思。正如我们在上面看到的,我相信它是 "let declaration and assignment" 的 shorthand。

无论如何,我不会太担心这个词。从您引用的段落中要理解的最重要的一点是 letconstvar 的更严格版本,在该语言的最新版本中引入以解决出现的陷阱和意外从 var 的工作方式。

Previously I thought the variable keyword and variable name together comprised a declaration

你是对的。

var a;
var b = 1;
let c;
let c = 2;
const d = 3;

这些都是变量的声明(尽管 const 技术变量不能改变,或者更准确地说,它们不能被重新分配)。

只是var有点草率,有点意外

您可以在同一范围内多次声明一个变量:

var a = 1;
var a = 2;

这不适用于 let:

let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared

var 的范围也令人惊讶:

for (var i = 0; i < 10; i++)
{
    var inner = 1;
}

console.log(inner); // prints 1 even though you might think this would be an error

或更糟:

for (var i = 0; i < 10; i++)
{
    for (var i = 0; i < 10; i++)
    {
        console.log('hello');
    }
}

乍一看,您可能认为这会打印 hello 100 次 (10*10),但实际上它只打印了 10 次,因为两个循环使用相同的变量。这是一种语言应该真正防止的程序员错误。如果该代码改用 let i,则会产生语法错误。

至于 提升 你可以认为它好像所有 var 声明都被移动到包含函数的顶部。

function foo()
{
    doThing();
    var i = 0;
    doSomethingElse();
    for (var j = 0; j < 10; j++)
    {
        var k = 10;
    }
}

尽管您可能会这样编写代码,但它的行为就像您编写的一样:

function foo()
{
    var i; // all declarations hoisted to top of containing function scope
    var j;
    var k;

    doThing();
    i = 0;
    doSomethingElse();
    for (j = 0; j < 10; j++)
    {
        k = 10;
    }
}

这就是为什么你可以这样写:

i = 10;
var i;

var 在代码中向上移动,因此它的行为如下:

var i;
i = 10;

你可以认为let没有移动。因此在声明之前引用它是错误的。

很抱歉在编写该 MDN 段落时使用了两个不同的术语。出于该文章的所有目的,"variable" 和 "binding" 应理解为同一事物。但让我们进入细节。

A variable declaration 创建变量(作为抽象实体)。它告诉编译器它应该引入一个新变量,还可以告诉它名称、要保存的类型、初始值、作用域等(取决于语言)。在JS中,有不同种类的声明做不同的事情,例如

  • var 有一个名称、一个可选的初始化程序和特定于 var
  • 的范围规则
  • function 有一个(有时是可选的)名称,该值总是给定的并且已知是一个函数
  • const 有一个名字,一个必需的初始化器,应该是不可变的,并且有词法范围

一个binding是变量名与变量实体的关联,例如“x指用class x声明的变量”。这种绑定取决于范围,即在每个不同的范围内都有不同的绑定,因此标识符 x 可能在不同的范围内引用不​​同的 things
鉴于 JavaScript 的作用域规则,变量声明也会导致在各自的作用域中为其自身创建绑定。

因此绑定使名称可供使用。这就是我所说的“let 绑定是在范围 的顶部创建的”。它与变量存在、分配内存或正在初始化无关。