顺便说一句,这些具有相同名称的 var 和函数声明的交互如何工作?

Ali*_*nou 3 javascript

谁能告诉我它是如何逐步工作的,以及为什么结果不同:

  1. 为什么在第一种情况下没有 SyntaxError:

第一的:

var a = 'foo'; 
function a() { };
console.log(a); // foo
Run Code Online (Sandbox Code Playgroud)

第二:

// Uncaught SyntaxError: Identifier 'a' has already been declared
{
    var a = 'foo'; 
    function a() { };
}
console.log(a);
Run Code Online (Sandbox Code Playgroud)

  1. 函数 a() {} 如何覆盖 var a?

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    console.log(a); // function a() {} - i guess that is the result of hoisting inside  the block but on this step  the global variable a is still 'foo'
    function a() {} // on this step -  the global variable a = function a() {}. How to explain that? 
    a = "bar"; //  on this step - overwrites only the local variable a 
    console.log(a); // bar
}
console.log(a); // function a() {}
Run Code Online (Sandbox Code Playgroud)

and if i write so:
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 8

TL;DR不要那样做。它的语义很复杂,并且(正如您所指出的)它们在严格模式和松散模式下是不同的。


  1. 为什么在第一种情况下没有 SyntaxError

因为这正是 JavaScriptvar和函数声明在 1995 年的设计方式。两者都在它们出现的函数或全局范围内创建了一个“绑定”(实际上是一个变量)。如果您对两者使用相同的名称,则重复使用相同的绑定。该函数首先被分配给它(因为函数声明被提升),但是当代码的逐步执行发生时,a = 'foo'部分var a = 'foo'运行并用'foo'.

第二

因为您在块中声明该函数,并且语义更新得多(ES2015);在此之前,每个 JavaScript 引擎都可以随心所欲地处理它(并且它们的处理方式不同,即使是同一个引擎也会将处理它们的方式从一个版本更改为另一个版本)。

那里的语义let是在块的顶部创建一个-style 绑定,并将函数分配给它。即使var没有块作用域,也不允许在块中声明var变量和let同名变量:

{
    // Not allowed
    let a = 1;
    var a = 2;
}
Run Code Online (Sandbox Code Playgroud)

let添加式的声明,这是不允许的,即使(再次)将var不包含该块。

考虑到这一点,以下是 JavaScript 引擎如何解释您的“第二个”代码的大致方式:

// Uncaught SyntaxError: Identifier 'a' has already been declared
{
    let a = function a() { };
    var a = 'foo'; 
}
console.log(a);
Run Code Online (Sandbox Code Playgroud)

由于这是相同的情况(同一块中的var-style 声明和let-style 声明),所以您不能这样做。

函数 a() {} 如何覆盖 var a?

又是那个隐含的本地let声明。以下是 JavaScript 引擎如何解释该函数声明的大致方式:

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    // Block-local binding and function creation are hoisted
    let a = function a() { };
    console.log(a); // function a() {}
    a<outer> = a<local>; // <======== Where the declaration was (not real syntax)
    a = "bar"; // only the local is set
    console.log(a); // bar
}
console.log(a); // function a() {}
Run Code Online (Sandbox Code Playgroud)

请注意函数声明最初所在的奇怪位:

a<outer> = a<local>; // Where the declaration was
Run Code Online (Sandbox Code Playgroud)

块中声明的位置控制何时将值分配给外部var. 是的,这很奇怪。因此,声明和函数创建被提升,然后在声明出现在代码中的地方完成对外部变量的赋值(即使通常情况下,函数声明相对于逐步代码的位置是无关紧要的)。重要的是,该赋值来自本地的当前值,即在逐步执行中到达该代码的位置。

在这一步 - 全局变量 a = bar。为什么不是函数 a() {}

因为赋值在块中的声明之前,并且本地a被复制到a声明所在的外部。以下是 JavaScript 引擎如何处理的大致方式:

// **only no use strict!!!**
var a = 'foo';
console.log(a); // foo
{
    let a = function a() { }; // Hoisted
    console.log(a);
    a = "bar";
    a<outer> = a<local>; // <======== Where the declaration was (not real syntax)
    console.log(a); // bar
}
console.log(a); // bar
Run Code Online (Sandbox Code Playgroud)

再说一遍,不要那样做。这真的很复杂而且很神秘。我为我最近的书JavaScript: The New Toys(第 3 章)写了一个完整的部分,但为了回答你的问题,我不得不回到那一章并提醒自己这是如何工作的。把这些脑细胞留到重要的事情上。