Redeclarable global eval-introduced vars S4
- Stage: Stage 4
- Status: Finished
- ECMAScript edition: ES2025
- Synchronized: Aug 28, 2026
- 中文译文 · Source repository
This proposal simplifies ECMAScript by removing the distinction between eval-introduced global var bindings and other global properties, allowing lexical redeclaration of eval-introduced vars at the global top level. It eliminates the [[VarNames]] tracking in the Global Environment Record, reducing the redeclaration error cases from three to two.
The README below comes from the upstream repository and may contain outdated stage or status metadata. Use the proposal details above as the current source of truth.
Make eval-introduced global vars redeclarable
Stage: 3
Author: Shu-yu Guo
Champion: Shu-yu Guo
Motivation
Currently, at the global toplevel, it is an error to:
- Redeclare a
varorfunctionbinding with aletorconstof the same name - Redeclare a non-configurable global property with a
letorconstof the same name - Redeclare a
letorconst with alet orconstof the same name
While all global var and function bindings are also global properties, (1) is distinct from (2) because var and function bindings introduced by sloppy direct eval at the global toplevel are configurable (unlike var bindings introduced via script, which are non-configurable).
This distinction is specified by tracking all var binding names in the [[VarNames]] field on the Global Environment Record.
This proposal's claim is that this distinction has dubious utility and complicates both the mental model and implementation of the language.
First, the goal of preventing redeclaration is not met anyway. var bindings introduced by eval are already delete-able because they are configurable. So it's not like you can't redeclare them. You can, you just first have to delete the var.
While it is true that eval-introduced var bindings in function scopes are also delete-able, the key difference is that function scopes are closed while the global toplevel scope is open. That is, contrast the following examples.
Second, the [[VarNames]] list has to be tracked specially, and is basically an extra bit on the property descriptor for all properties on the global object. This is extra implementation complexity.
Proposal
See https://github.com/tc39/ecma262/pull/3226 for the spec changes (rendering). That PR removes [[VarNames]], effectively reducing the 3 cases above to 2:
- Redeclare a non-configurable global property with a
letorconstof the same name - Redeclare a
letorconstwith aletorconstof the same name
The alternative of making sloppy direct eval-introduced vars non-configurable is not considered owing to a much longer history, and its being less likely to be a web compatible change.
Consequences
The main thing that changes is that the following snippet is now allowed. An eval-introduced var in the global toplevel scope can be redeclared by lexical bindings, effectively shadowing the configurable property on globalThis.
It is web compatible to make this change as we are changing a throwing behavior to a non-throwing behavior.
FAQ
Does this break symmetry with function-scope vars and direct eval?
Not really, because function scopes and the global toplevel scope already behave very differently.
Per above, the global toplevel is an open scope while function scopes are closed. This proposal changes observable behavior that is only observable by re-entering the global scope (e.g. via new <script> tags in the HTML embedding). In other words, if one were to use the global top-level scope like a closed scope and does not re-enter it, there is no observable difference.