JS Tip of the Day: __proto__ in Object Literals

__proto__ in Object Literals
Version: ES2015 (officially)
Level: Intermediate

Normally, for access to an object’s prototype, you’d use Object.getPrototypeOf() and Object.setPrototypeOf(). But there’s also the deprecated __proto__ accessor property for objects that inherit from the Object type that can also give you the same kind of access. That __proto__ identifier is not only accessible from existing object values but can also be used directly in object literals to set the prototype for the object being created.

let defaults = { color: 'red' };
let styles = {
    fontSize: 'large',
    __proto__: defaults // sets prototype
};

console.log(styles.color); // red
console.log(Object.getPrototypeOf(styles) === defaults); // true

When used this way, it does not invoke the __proto__ accessor. It’s actually something else entirely (remember, object literal values are defined, not assigned). Instead, there’s a special case made specifically for the handling of this property when used directly within an object literal by name. Instead of becoming a property, the value of __proto__ is removed and used as though the object were created using Object.create(__proto__).

This behavior only applies when using a non-computed version of this identifier. If you were to use a computed property, or otherwise dynamically add the property through something like a spread, the prototype would not be set. You have to explicitly include the property in the source code by name. This prevents accidental prototype assignments through dynamic keys.

let defaults = { color: 'red' };
let styles = {
    fontSize: 'large',
    ['__proto__']: defaults // sets property named __proto__
};

console.log(styles.color); // undefined
console.log(Object.getPrototypeOf(styles) === defaults); // false
console.log(styles.__proto__); // {color: "red"}

Though the __proto__ property is in the ECMAScript specification as part of the standard, it’s only specified as a feature for web browsers for legacy reasons. Non-browser clients can decide not to support __proto__ and still be considered standard compliant. As a result, you might not want to rely on using __proto__, as seen here, or as an accessor property.

More info: