使用var与let / const进行块级变量重新声明

时间:2017-12-29 20:52:23

标签: javascript variables ecmascript-6

第1部分

给出这个例子:

var number = 10
{
   var number = 42
}
console.log(number) // 42

为什么第4行不会抛出Uncaught SyntaxError: Identifier 'number' has already been declared?它适用于let / const,因为块范围(虽然输出当然是10而不是42),但它如何与{{1}一起使用}?

第2部分

将此与以下内容进行比较,该内容适用于var

var

但与var number = 10 var number = 42 console.log(number) // 42 无关:

let

为什么let number = 10 let number = 42 // SyntaxError console.log(number) 会获得"免费通行证"?在使用var时,是否与number属性重新分配给窗口对象有关?

2 个答案:

答案 0 :(得分:6)

您可以在JavaScript中重新声明var个变量,即使在严格模式下

  

使用var声明的变量的范围是其当前的执行上下文,它是封闭函数,或者对于在任何函数外部声明的变量,global。如果你重新声明一个JavaScript变量,它就不会失去它的值。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting



'use strict'
var someVar = 'Hello';
var someVar = 2 + 2;
console.log(someVar);




答案 1 :(得分:6)

为什么第4行不会抛出Uncaught SyntaxError: Identifier 'number' has already been declared

作为Sébastien already mentioned,使用var声明的变量在当前执行上下文中声明,并且可以重新声明。可以将执行上下文的概念与框进行比较。根据{{​​3}}:

  

8.3执行上下文

     

执行上下文是一种规范设备,用于跟踪ECMAScript实现对代码的运行时评估。在任何时间点,每个代理程序最多只有一个执行上下文实际执行代码。这称为代理程序的运行执行上下文

     

[...]

     

ECMAScript代码的执行上下文具有表22中列出的其他状态组件。

     表22:ECMAScript代码执行上下文的附加状态组件
 Component           Purpose

LexicalEnvironment Identifies the Lexical Environment used to resolve identifier references made by code within this execution context. VariableEnvironment Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context.

所以每当你编写JavaScript代码时,它就被分成几个小的“盒子”,称为执行上下文,只要解释器​​ECMAScript Language Specification Section 8.3在每个框中都有很多组件,但特别是 LexicalEnvironment VariableEnvironment 。这些都是父执行上下文“框”内的“迷你框”,它们保存对代码可以访问的当前执行上下文中声明的变量的引用。 LexicalEnvironment 是指使用letconst声明的变量。 VariableEnvironment 是指使用var创建的变量。

现在看encounters a new syntactic structure such as a block, function declaration, etc.

  

13.3.2变量声明

     

注意var语句声明了作用于正在运行的执行上下文的VariableEnvironment的变量。 Var变量在实例化包含词法环境时创建,并在创建时初始化为未定义在任何VariableEnvironment的范围内,常见的 BindingIdentifier 可能会出现在多个 VariableDeclaration 中,但这些声明只能共同定义一个变量。

引用说明的最后一部分说明了您可以多次声明var的原因。每次解释器遇到一个函数时,都会创建一个新的 VariableEnvironment ,因为var是函数范围的,如果你在全局范围内,则有一个全局 VariableEnvironment 的。在您的情况下,您已在全局范围内声明number两次,因为{ … }是一个块,而不是一个函数,但它们共同只定义number一次。因此,您的代码实际上与此相同:

var number = 10 //declared once
{
  number = 42 //since { … } does not create a new VariableEnvironment, number is the same 
              //variable as the one outside the block. Thus, the two declarations only define
              //number once and every redeclaraction is essentially reassignment.
}
console.log(number) //42

正如你所说,letconst是块范围的。它们不会抛出错误,因为{ … }会创建一个新块。

为什么var获得“免费通行证”?是否在使用number时将var属性重新分配给窗口对象?

如前所述,var声明可以在 VariableEnvironment 中多次出现 - 但let和{{{ 1}}。作为Section 13.3.2,ECMAScript的作者直到很久以后才意识到有这么糟糕的声明者const的垮台和怪癖,改变var的行为会导致整个互联网崩溃,因为向后兼容性是ECMAScript / JavaScript的一个重要方面。因此,作者引入了新的声明符varlet,它们的目标是块范围和可预测的,更像您在其他语言中看到的其他声明符。因此,只要在同一范围内有另一个声明,constlet声明就会抛出错误。这与const无关,只是因为兼容性和历史问题。