咨询ECMAScript的CreateGlobalFunctionBinding算法步骤及赋值问题
Great question — this algorithm's interplay between environment records and the global object can feel counterintuitive at first, especially the repeated assignments. Let's break down each step you're asking about, and clear up why that "duplicate" step 9 is actually critical.
First, a quick context: CreateGlobalFunctionBinding(N, V, D) is the ECMAScript spec algorithm that creates special global function bindings (like built-ins such as eval, parseInt, or fetch). These bindings exist in two places: the global environment record (the scope layer) and the global object (e.g., window in browsers, global in Node.js), and they need to stay in sync.
Step 5: Create the mutable binding in the global environment record
This step creates a mutable binding for the name N in the declarative portion of the global environment record. The D parameter controls whether this binding is configurable: if D is true, you can later delete the binding with delete N; if false, the binding is permanent.
This is a scope-level setup — it establishes that N exists in the global scope, separate from the global object's properties. When you access N in global code, the JS engine checks this environment record first, not the global object directly.
Step 7: Define the property on the global object
This step adds a data property N to the global object with the following descriptor:
{ [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }
This makes the function accessible both as a global variable (N) and as a property of the global object (window.N). At this point, the environment record binding and the global object property are two separate entities with the same value (V), but they aren't yet linked to sync updates.
Step 9: Activate bidirectional sync between binding and global object property
At first glance, this step looks identical to Step 6 (both call globalEnv.SetMutableBinding(N, V, false)), but it serves a unique, critical purpose:
The global environment record's SetMutableBinding method has special logic: when updating a declarative binding, if the global object already has a matching property, it synchronizes the value in both places and establishes a bidirectional sync link.
- In Step 6, we called
SetMutableBindingbefore the global object had theNproperty, so it only updated the environment record's binding value (no sync was possible). - In Step 9, we call it after the global object's property exists. This triggers the sync logic:
- It confirms the environment record's binding value is still
V(a safeguard against unexpected changes during prior steps). - Most importantly, it sets up the link so future changes to either the binding (
N = newFunc) or the global object property (window.N = newFunc) automatically update the other.
- It confirms the environment record's binding value is still
Without Step 9, the two entities would stay disconnected — modifying one wouldn't affect the other, which violates the expected behavior of global built-in functions.
Why two assignments (Step 6 and Step 9)?
They aren't redundant — each serves a distinct purpose:
- Step 6: Initializes the scope-level binding value before the global object property exists. This ensures the name
Nis recognized in the global scope from the start. - Step 9: Activates the sync mechanism between the binding and the global object property. It's not just setting a value — it's linking the two entities to stay in sync for all future changes.
内容的提问来源于stack exchange,提问作者Дмитрий Порховнюк

