令人困惑的 EcmaScript 规范 - 声明绑定实例化

Confusing EcmaScript Specification - Declaration Binding Instantiation


当我阅读 learn "hoisting" 规范时,FunctionDeclaration 发生在 step 5VariableDeclaration发生了第 8 步,我认为这意味着 函数声明 变量声明 具有更高的优先级。
你认为为什么函数声明要声明拳头,这有什么原因吗?
注意:你可以从这个linkhttp://www.ecma-international.org/ecma-262/5.1/#sec-10.5

阅读这部分内容

我对我的问题有假设。这不是答案,这是假设,因为我真的不知道这个问题的答案,显然人们也不知道答案。
我的假设是
函数可以覆盖另一个函数,但我认为变量不能,因为第一个 functionDeclaration 发生了。

有两种选择,或者此代码无效(例如,语法错误):

function foo() {
    console.log("foo1");
}
var foo = function() {
    console.log("foo2");
};
foo();

...或者说事物是有规律的。在 JavaScript 规范中,在这种情况下,事物有一个顺序:

  1. 函数声明在记录 foo1.
  2. foo 范围内创建了一个绑定
  3. var foo = ... 中的 var 无效,因为 varAlreadyDeclared 为真。
  4. foo 被函数表达式 foo = function() { /*...*/ };
  5. 的结果覆盖

为什么?因为它就是这样指定的。更具体地说,因为函数声明和 var 语句都在执行上下文中创建了一个 可变绑定 ,所以无论哪个先到达那里,都会创建绑定。 "First" 再次需要定义,因此选择的定义是声明首先创建绑定。但是两者都创建可变绑定,请记住 var foo 部分和 foo = ... 部分是完全分开处理的。由于函数声明是在逐步执行代码之前处理的,因此下面的代码与上面的完全相同:

var foo = function() {
    console.log("foo2");
};
function foo() {
    console.log("foo1");
}
foo();

虽然函数声明首先创建绑定并将其设置为执行 console.log("foo1") 的函数,但 foo = ... 部分随后发生并更新绑定,以便它引用执行的新函数console.log("foo2").

Brendan Eich 有其他选择,但这是他在 1995 年 5 月那决定命运的 10 天里做出的选择。:-) 确实 符合这些选择决定:

  1. 函数声明创建可变绑定
  2. 函数声明和 var 声明创建相同种类 的绑定
  3. 提升函数声明(例如,在逐步执行开始之前创建函数并将其分配给绑定)
  4. vardeclaration 部分被提升,使用初始化值 undefined,但是 initializer部分var x = y直到代码一步步执行才设置

函数声明和变量声明都被提升

首先提升函数,然后是变量。

foo(); // 1

var foo;

function foo() {
  console.log(1);
}

foo = function() {
  console.log(2);
};

请注意 var foo 是重复的(因此被忽略的)声明,即使它出现在函数声明之前。

此外,一个有趣的事实是后续的函数声明确实覆盖了之前的声明。

foo(); // 3

function foo() {
  console.log(1);
}

var foo = function() {
  console.log(2);
};

function foo() {
  console.log(3);
}

您可以在 You Don't Know JS: Scope & Closures 书中找到更多信息