Understanding ECMAScript Property Access: The [[Get]] Internal Method and Prototype‑Chain Walk
This article explains how the ECMAScript specification defines property access through the [[Get]] internal method, walks the prototype chain, and uses the Receiver value, illustrating each step with concrete code examples and the relevant abstract operations such as OrdinaryGet and GetValue.
The purpose of this translation is to provide consistent Chinese terminology for core ECMAScript concepts, using the V8 blog post "Understanding ECMAScript – Part 2" as the source.
When accessing an object property, JavaScript walks the prototype chain: if the property is not found on the object itself, the lookup continues up the chain until the property is found or a null prototype is reached.
const o1 = { foo: 99 };
const o2 = {};
Object.setPrototypeOf(o2, o1);
o2.foo; // → 99The walk is defined by the object internal methods, especially [[Get]] and [[GetOwnProperty]] . For ordinary objects, [[Get]] delegates to the abstract operation OrdinaryGet :
[[Get]](P, Receiver) Return ? OrdinaryGet(O, P, Receiver) .
OrdinaryGet performs the following steps (simplified):
OrdinaryGet(O, P, Receiver) Assert IsPropertyKey(P) is true. Let desc = ? O.[[GetOwnProperty]](P) . If desc is undefined, retrieve the prototype parent = ? O.[[GetPrototypeOf]]() and, if not null, return ? parent.[[Get]](P, Receiver) ; otherwise return undefined. If IsDataDescriptor(desc) is true, return desc.[[Value]] . Assert IsAccessorDescriptor(desc) is true, let getter = desc.[[Get]] , and if getter is undefined return undefined; otherwise return ? Call(getter, Receiver) .
The Receiver argument is only used when the property is an accessor. During the call to the getter, Receiver becomes the this value, which is the original object from which the property lookup started.
The reference created by the member expression is built by the abstract operation GetValue , which distinguishes between primitive values and property references. For the expression o2.foo , the reference has base o2 , name "foo" , and a non‑strict flag.
const o1 = { x: 10, get foo() { return this.x; } };
const o2 = { x: 50 };
Object.setPrototypeOf(o2, o1);
o2.foo; // → 50When the getter is invoked, this refers to o2 , not o1 , so the returned value is o2.x .
The runtime semantics for a member expression MemberExpression . IdentifierName are defined as:
Evaluate the base reference. Obtain baseValue = ? GetValue(baseReference) . Determine strict mode. Return ? EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict) .
EvaluatePropertyAccessWithIdentifierKey creates a reference with the base value, the identifier string, and the strict flag, which is then passed to GetValue to finally retrieve the property.
Argument lists also use GetValue : the expression o2.foo is evaluated as an AssignmentExpression , producing a reference that is then resolved to a value before being passed to the function call.
In summary, the ECMAScript specification defines property access through a series of abstract operations that walk the prototype chain, invoke internal methods, and correctly bind the this value via the Receiver, allowing developers to predict the behavior of code like the examples shown.
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.