Block-level variable redeclaration with var vs. let/const

那年仲夏 提交于 2021-01-27 04:10:47

问题


Part 1

Given this example:

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

Why does line 4 not throw an Uncaught SyntaxError: Identifier 'number' has already been declared? It works with let / const because of block scoping (although the output is, of course, 10 not 42), but how come it works with var?

Part 2

Compare this to the following, which works with var:

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

but doesn't with let:

let number = 10
let number = 42 // SyntaxError
console.log(number)

Why is var given a "free pass"? Does it have to do with the number property being reassigned to the window object when var is used?


回答1:


You are allowed to redeclare var variables in JavaScript, even in strict mode.

The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value.

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);



回答2:


Why does line 4 not throw an Uncaught SyntaxError: Identifier 'number' has already been declared?

As Sébastien already mentioned, variables declared with var are declared in the current execution context and can be redeclared. The concept of an execution context can be compared to a box. Per the ECMAScript Language Specification Section 8.3:

8.3 Execution Contexts

An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation. At any point in time, there is at most one execution context per agent that is actually executing code. This is known as the agent's running execution context.

[...]

Execution contexts for ECMAScript code have the additional state components listed in Table 22.

Table 22: Additional State Components for ECMAScript Code Execution Contexts
 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.

So everytime you write JavaScript code, it's separated into small individual "boxes" called execution contexts that are created whenever the interpreter encounters a new syntactic structure such as a block, function declaration, etc. In each of these boxes, there are many components, but in particular the LexicalEnvironment and VariableEnvironment. These are both "mini-boxes" inside the parent execution context "box" that hold references to the variables declared inside the current execution context that the code may access. LexicalEnvironment refers to the variables declared with let and const. VariableEnvironment refers to variables created with var.

Now looking at Section 13.3.2:

13.3.2 Variable Statement

NOTE      A var statement declares variables that are scoped to the running execution context's VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created. Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collectively define only one variable.

The last part of the quoted note states the reason why you can declared a var more than once. Every time the interpreter encounters a function, a new VariableEnvironment is created because var is function-scoped, and if you're in the global scope there is one global VariableEnvironment. In your case, you've declared number both times in the global scope because { … } is a block, not a function, but they collectively only define number once. So, your code actually performs the same as this:

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

And as you said, let and const are block-scoped. They do not throw an error because { … } creates a new block.

Why is var given a "free pass"? Does it have to do with the number property being reassigned to the window object when var is used?

As described before, a var declaraction can occur many times within a VariableEnvironment - but the same doesn't hole true for let and const. As Bergi mentioned, the ECMAScript authors hadn't realized the downfalls and quirks of having such a bad declarator var until much later, and changing var's behavior would cause the whole internet to collapse, as backwards-compatibility is a huge aspect of ECMAScript/JavaScript. Thus, the authors introduced new declarators, let and const that would aim to be block-scoped and predictable, more like other declarators you see in other languages. As such, let and const declarations throw an error whenever there is another declaration within the same scope. This has nothing to do with window, just because of compatibility and historical issues.



来源:https://stackoverflow.com/questions/48028320/block-level-variable-redeclaration-with-var-vs-let-const

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!