Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add __proto__ key initializer to "Inheritance and the prototype chain" #16090

Merged
merged 9 commits into from
May 17, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ function f() {
// Functions inherit from Function.prototype
// (which has methods call, bind, etc.)
// f ---> Function.prototype ---> Object.prototype ---> null

const p = { b: 2, __proto__: o };

// It is possible to point the newly created object's [[Prototype]] to
// another object via the __proto__ literal property. (Not to be confused
// with Object.prototype.__proto__ accessors)
// p ---> o ---> Object.prototype ---> null
```

### With a constructor
Expand Down Expand Up @@ -396,7 +403,7 @@ Note: It is **not** enough to check whether a property is [`undefined`](/en-US/d

### Summary of methods for extending the prototype chain

Here are all 4 ways and their pros/cons. All of the examples listed below create exactly the same resulting `inst` object (thus logging the same results to the console), except in different ways.
Here are all 5 ways and their pros/cons. All of the examples listed below create exactly the same resulting `inst` object (thus logging the same results to the console), except in different ways.

#### #1: New initialization

Expand Down Expand Up @@ -480,7 +487,7 @@ const proto = Object.create(
bar.prototype = proto;
const inst = new bar();
console.log(inst.foo_prop);
console.log(inst.bar_prop)
console.log(inst.bar_prop);
```

<table class="standard-table">
Expand Down Expand Up @@ -540,7 +547,7 @@ const proto = Object.setPrototypeOf(
bar.prototype = proto;
const inst = new bar();
console.log(inst.foo_prop);
console.log(inst.bar_prop)
console.log(inst.bar_prop);
```

<table class="standard-table">
Expand All @@ -559,28 +566,30 @@ console.log(inst.bar_prop)
<tr>
<th scope="row">Con(s)</th>
<td>
Ill-performing. Should be deprecated. Many browsers optimize the
prototype and try to guess the location of the method in memory when
calling an instance in advance; but setting the prototype dynamically
disrupts all those optimizations. It might cause some browsers to
recompile your code for de-optimization, to make it work according to
the specs. Not supported in IE8 and below.
Ill-performing. Should be avoided if it's possible to set the prototype
at object creation time. Many browsers optimize the prototype and try to
guess the location of the method in memory when calling an instance in
advance; but setting the prototype dynamically disrupts all those
optimizations. It might cause some browsers to recompile your code for
de-optimization, to make it work according to the specs. Not supported
in IE8 and below.
</td>
</tr>
</tbody>
</table>

#### #4: Setting the {{jsxref("Object/proto","__proto__")}} property

> **Warning:** `Object.prototype.__proto__` accessors are **non-standard** and deprecated. You should almost always use `Object.setPrototypeOf` instead.

```js
// Technique 1
function A() {}
A.prototype.foo_prop = 'foo val';
function bar() {}
const proto = {
bar_prop: 'bar val',
__proto__: A.prototype
};
const proto = { bar_prop: 'bar val' };
// DON'T USE THIS: for example only.
proto.__proto__ = A.prototype;
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
bar.prototype = proto;
const inst = new bar();
console.log(inst.foo_prop);
Expand All @@ -589,17 +598,12 @@ console.log(inst.bar_prop);

```js
// Technique 2
const inst = {
__proto__: {
bar_prop: 'bar val',
__proto__: {
foo_prop: 'foo val',
__proto__: Object.prototype
}
}
};
const inst = {};
// DON'T USE THIS: for example only.
inst.__proto__ = { bar_prop: 'bar val' };
inst.__proto__.__proto__ = { foo_prop: 'foo val' };
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
console.log(inst.foo_prop);
console.log(inst.bar_prop)
console.log(inst.bar_prop);
```

<table class="standard-table">
Expand All @@ -624,7 +628,59 @@ console.log(inst.bar_prop)
instance in advance; but setting the prototype dynamically disrupts all
those optimizations and can even force some browsers to recompile for
de-optimization of your code, to make it work according to the specs.
Not supported in IE10 and below.
Not supported in IE10 and below. The {{jsxref("Object/proto","__proto__")}}
setter is normative optional, so it may not work across all platforms.
You should almost always use {{jsxref("Object.setPrototypeOf")}}
instead.
</td>
</tr>
</tbody>
</table>

#### #5: Using the `__proto__` key in object initializers

> **Note:** This is not to be confused with the aforementioned `Object.prototype.__proto__` accessors. `__proto__` in object literals is standardized and optimized.

```js
const inst = {
__proto__: {
bar_prop: 'bar val',
__proto__: {
foo_prop: 'foo val',
// This can be omitted
__proto__: Object.prototype
}
}
};
console.log(inst.foo_prop);
console.log(inst.bar_prop);
```

<table class="standard-table">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to other reviewers: the reason this is an HTML table here rather than a Markdown table is that the corresponding existing sections of this article use similar HTML tables — so this is following the existing structure.

Probably we should replace all the HTML tables in the docs with Markdown tables, but that’s outside the scope of this patch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, I'm not entirely sure how that can be converted to a Markdown table: is there a syntax for <caption>?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, I'm not entirely sure how that can be converted to a Markdown table: is there a syntax for <caption>?

The equivalent is to just have a paragraph that precedes the table.

<caption>
Pros and cons of using the <code>__proto__</code> key in <a href="/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer">object initializers</a>
</caption>
<tbody>
<tr>
<th scope="row">Pro(s)</th>
<td>
Supported in all modern browsers. Pointing the <code>__proto__</code>
key to something that is not an object only fails silently without
throwing an exception. Contrary to the
{{jsxref("Object/proto", "Object.prototype.__proto__")}} setter,
<code>__proto__</code> in object literal initializers is standardized
and optimized, and can even be more performant than
{{jsxref("Object.create")}}. Declaring extra own properties on the
object at creation is more ergonomic than
{{jsxref("Object.create")}}.
</td>
</tr>
<tr>
<th scope="row">Con(s)</th>
<td>
Not supported in IE10 and below. Likely to be confused with
{{jsxref("Object/proto", "Object.prototype.__proto__")}} accessors for
people unaware of the difference.
</td>
</tr>
</tbody>
Expand Down