Understanding Prototype Property Lookup and the [[Get]] Internal Method in ECMAScript
This article walks through how the ECMAScript specification defines prototype chain property lookup, detailing the [[Get]] internal method, OrdinaryGet algorithm, the role of the Receiver parameter, and how these mechanisms are invoked during member expression evaluation and function calls.
We begin by recalling that property access in JavaScript follows the prototype chain: if an object lacks the requested property, the engine walks up the chain until it finds the property or reaches a null prototype.
The specification defines this behavior through the [[Get]] internal method. Ordinary objects delegate [[Get]] to the abstract operation OrdinaryGet , while exotic objects may provide their own implementation.
OrdinaryGet ( O, P, Receiver ) is specified as:
1. Assert IsPropertyKey(P) is true.
2. Let desc be ? O.[[GetOwnProperty]](P).
3. If desc is undefined, then
a. Let parent be ? O.[[GetPrototypeOf]]().
b. If parent is null, return undefined.
c. Return ? parent.[[Get]](P, Receiver).
4. If IsDataDescriptor(desc) is true, return desc.[[Value]].
5. Assert IsAccessorDescriptor(desc) is true.
6. Let getter be desc.[[Get]].
7. If getter is undefined, return undefined.
8. Return ? Call(getter, Receiver).The Receiver argument is the this value used when a getter function is invoked. It originates from the original reference created by the MemberExpression evaluation.
The abstract operation GetValue(V) processes a reference by first obtaining its base object, then calling base.[[Get]](propertyName, GetThisValue(V)) . The base is determined by the runtime semantics of MemberExpression : MemberExpression . IdentifierName :
1. Let baseReference be the result of evaluating MemberExpression.
2. Let baseValue be ? GetValue(baseReference).
3. Determine strict mode flag.
4. Return ? EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict).EvaluatePropertyAccessWithIdentifierKey creates a reference whose base is the coerced object value and whose referenced name is the identifier string. This reference is later passed to GetValue , triggering the prototype‑chain walk described above.
Example of a simple data property lookup:
const o1 = { foo: 99 }; const o2 = {}; Object.setPrototypeOf(o2, o1); o2.foo; // → 99Example of an accessor property where the this value matters:
const o1 = { x: 10, get foo() { return this.x; } }; const o2 = { x: 50 }; Object.setPrototypeOf(o2, o1); o2.foo; // → 50When the accessor is invoked, the Receiver is the original object ( o2 ) from which the property lookup started, not the prototype object ( o1 ) that actually defines the getter.
The article also shows how member expressions are treated as arguments in function calls, with ArgumentListEvaluation ultimately invoking GetValue on each argument, ensuring the same prototype‑lookup semantics apply.
In summary, the ECMAScript spec ties together grammar productions, abstract operations, and internal methods to precisely define how property access works, why [[Get]] is invoked, and how the Receiver is propagated through the prototype chain.
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.