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.