> For the complete documentation index, see [llms.txt](https://strctr.gitbook.io/programming/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://strctr.gitbook.io/programming/01-languages/javascript/01-language/b-structured/b2-objects/09-object-prototype.md).

# 09. Prototype Chain

[Inheritance and the prototype chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)

Each object has a private property (referred to as `[[Prototype]]` ) which holds a link to another object called its **prototype**. That prototype object has a prototype of its own, and so on until an object is reached with `null` as its prototype. By definition, `null` has no prototype, and acts as the final link in this **prototype chain**.

## Define object prototype via `__proto__`

Если один объект имеет специальную ссылку `__proto__` на другой объект, то при чтении свойства из него, если свойство отсутствует в самом объекте, оно ищется в объекте `__proto__`.

```javascript
const a = { magic: 42 };
const b = { __proto__: a, super: 55 };
b.super; // => 55
b.magic; // => 42
b.constructor; // => Object
b.__proto__; // => {magic: 42}
b.isPrototypeOf(a); // => false
a.isPrototypeOf(b); // => true
a.__proto__ === Object.prototype; // => true
```

Notice that the usage of `object.__proto__` as a getter/setter is not recommended. Alternatives `Object.getPrototypeOf()` and `Object.setPrototypeOf()` or `Object.create(prototype)` should be considered instead.

* `Object.getPrototypeOf(obj)` - method returns the direct prototype (i.e. the value of the internal `[[Prototype]]` property) of the specified object. `TypeError` if `obj` is not an object.
* `Object.setPrototypeOf(obj, prototype)` - method sets the prototype of a specified object to another object or `null`.

The good part is that ES2015 allows to use the literal `__proto__` as the property name to setup the prototype right in the object literal `{ __proto__: protoObject }`. JavaScript constraints to use only an object or null as a value for `__proto__` property.

```javascript
const myProto = {
  propertyExists(name) {
    return name in this;
  }
};

const myNumbers = {
  __proto__: myProto,
  array: [1, 6, 7]
};

myNumbers.propertyExists("array"); // => true
myNumbers.propertyExists("collection"); // => false
```

The `__proto__` property may not be supported. A simple check for the getter/setter:

```javascript
// Simple check
const supported = {}.hasOwnProperty.call(Object.prototype, "__proto__");

// A more sophisticated check:
const desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
const supported =
  typeof desc.get === "function" && typeof desc.set === "function";
```

Recommendations:

* Use `Object.getPrototypeOf()` to get the prototype of an object.
* Prefer `Object.create()` to create a new object with a given prototype. Avoid `Object.setPrototypeOf()`, which hampers performance on many engines.
* I actually like `__proto__` as an operator in an object literal. It is useful for demonstrating prototypal inheritance and for creating dictionary objects.

## Access to object prototype via `super`

В ES2015 появилось новое ключевое слово `super`, предназначенное только для использования в методах объекта. Вызов `super.parentProperty` позволяет из метода объекта получить свойство его прототипа:

```javascript
const animal = {
  walk() {
    console.log("i am walking");
  }
};

const rabbit = {
  __proto__: animal,

  walk() {
    super.walk();
    console.log("rabbit walk");
  }
};
```

Свойство `super` работает через прототип только в методах объекта. При обращении через `super` используется `[[HomeObject]]` текущего метода, и от него берётся `__proto__`. В частности, если переписать этот код, оформив `rabbit.walk` как обычное свойство-функцию, то будет ошибка. Исключением из этого правила являются функции-стрелки. В них используется `super` внешней функции. Например, здесь функция-стрелка в `setTimeout` берёт внешний `super`:

```javascript
const animal = {
  walk() {
    console.log("I'm walking");
  }
};

const rabbit = {
  __proto__: animal,
  walk() {
    setTimeout(() => super.walk()); // I'm walking
  }
};

rabbit.walk();
```

## Check prototype chain with `obj.isPrototypeOf()`

The `proto.isPrototypeOf(obj)` method checks if an `proto` exists in `obj` object's prototype chain.

```javascript
const proto = {};
const obj = Object.create(proto);
const obj2 = Object.create(obj);

Object.getPrototypeOf(obj) === proto; // => true
Object.getPrototypeOf(obj2) === obj; // => true
Object.getPrototypeOf(obj2) === proto; // => false

obj2.isPrototypeOf(proto); // => false
proto.isPrototypeOf(obj2); // => true

Number.prototype.isPrototypeOf(2); // => false
Number.prototype.isPrototypeOf(new Number(2)); // => true
Number.prototype.isPrototypeOf(Object(2)); // => true
```

## Access to properties

Доступ к любым свойствам объекта выполняется в контексте (наследования) цепочки прототипов.

> Предположим, что программа обращается к свойству `x` объекта `o`. Если объект `o` не имеет собственного свойства с таким именем, выполняется попытка отыскать свойство `x` в прототипе объекта `o`. Если объект-прототип не имеет собственного свойства с этим именем, но имеет свой прототип, выполняется попытка отыскать свойство в прототипе прототипа. Так продолжается до тех пор, пока не будет найдено свойство `x` или пока не будет достигнут объект, не имеющий прототипа.

В цепочке прототипов, свойство объекта с определенным именем перекрывает свойство с таким же именем в его цепочке прототипов, если перекрытие не запрещено.

```javascript
const proto = {
    describe() {
        return 'name: '+ this.name;
    }
};
const obj={
    __proto__: proto,
    name: 'obj'
};

> obj.describe
[Function]

> obj.describe();
"name: obj"

> obj.describe = function () { return 'overridden' };
> obj.describe()
'overridden'
```

При присваивании свойства всегда меняется текущий объект и никогда прототип. Присваивание свойства может не выполниться в некоторых случаях.

Операция присваивания значения свойству проверит наличие этого свойства в цепочке прототипов, чтобы убедиться в допустимости присваивания. Например, если объект `o` наследует свойство `x`, доступное только для чтения, то присваивание выполняться не будет. Однако если присваивание допустимо, всегда создается или изменяется свойство в оригинальном объекте и никогда в цепочке прототипов.&#x20;

Тот факт, что механизм наследования действует при чтении свойств, но не действует при записи новых значений, является ключевой особенностью языка JavaScript, потому что она позволяет выборочно переопределять унаследованные свойства.&#x20;

Существует одно исключение из этого правила, когда операция присваивания значения свойству терпит неудачу. Если объект `o` наследует свойство `x` и доступ к этому свойству осуществляется посредством методов доступа, то вместо создания нового свойства `x` в объекте o производится вызов метода записи нового значения. Однако обратите внимание, что метод записи вызывается относительно объекта `o`, а не относительно прототипа, в котором определено это свойство, поэтому, если метод записи определяет какие-либо свойства, они будут созданы в объекте `o`, а цепочка прототипов опять останется неизменной.

## Dictionaries

Объекты без прототипа часто называются *картами* или *dictionary pattern*:

```javascript
const dict = Object.create(null);
```

Преимущество таких объектов в том, что они концептуально более проще, чем объекты, использующиеся как карты. Для них можно безопасно использовать все операторы над объектами, без различных проверок, связанных с цепочкой прототипов.
