Прототипы, наследование
В программировании часто возникает необходимость что-то расширить.
Например у нас есть объект user со своими свойствами и методами, и мы хотим создать объекты admin и guest как его
слегка измененные варианты. Мы хотели бы повторно использовать то, что есть у объекта user, не копировать его методы,
а создать новый объект на его основе.
Прототипное наследование — это возможность языка, которая помогает в этом.
[[Prototype]]
В JavaScript объекты имеют специальное скрытое свойство [[Prototype]], которое либо равно null, либо ссылается на
другой объект. Этот объект называется «прототип».
Когда мы хотим прочитать свойство из object, а оно отсутствует, JavaScript автоматически берет его из прототипа.
Свойство [[Prototype]] является внутренним и скрытым, но есть много способов задать его.
Один из способов — использование __proto__:
let animal = {
  eats: true
}
let rabbit = {
  jumps: true
}
rabbit.__proto__ = animal; // (*)
// теперь мы можем найти оба свойства в rabbit:
console.log(rabbit.eats);  // -> true (**)
console.log(rabbit.jumps); // -> true
Здесь строка (*) устанавливает animal как прототип для rabbit.
Затем, когда console.log пытается прочитать свойство rabbit.eats (**), его нет в rabbit, поэтому JavaScript
следует по ссылке [[Prototype]] и находит его в animal.
Так что если у animal много полезных свойств и методов, то они автоматически становятся доступными у rabbit. Такие
свойства называются «унаследованными».
Если у нас есть метод в animal, он может быть вызван на rabbit:
let animal = {
  eats: true,
  walk() {
    console.log('Animal walk');
  }
}
let rabbit = {
  jumps: true,
  __proto__: animal
}
// walk взят из прототипа
rabbit.walk(); // -> Animal walk
Метод автоматически берется из прототипа. Цепочка прототипов может быть длиннее:
let animal = {
  eats: true,
  walk() {
    console.log('Animal walk');
  }
}
let rabbit = {
  jumps: true,
  __proto__: animal
}
let longEar = {
  earLength: 10,
  __proto__: rabbit
}
// walk взят из цепочки прототипов
longEar.walk(); // -> Animal walk
console.log(longEar.jumps); // -> true (из rabbit)
Есть некоторые ограничения:
- Ссылки не могут идти по кругу.
 - Значение 
__proto__может быть объектом илиnull. Другие типы игнорируются. - Может быть только один 
[[Prototype]]. Объект не может наследоваться от двух других объектов. 
Операция записи не использует прототип
Прототип используется только для чтения свойств.
Операции записи/удаления работают напрямую с объектом.
В приведенном ниже примере мы присваиваем rabbit собственный метод walk:
let animal = {
  eats: true,
  walk() {
    /* этот метод не будет использоваться в rabbit */
  }
};
let rabbit = {
  __proto__: animal
};
rabbit.walk = function () {
  console.log("Rabbit! Bounce-bounce!");
};
rabbit.walk(); // -> Rabbit! Bounce-bounce!
Теперь вызов rabbit.walk() находит метод непосредственно в объекте и выполняет его, не используя прототип.
Значение this
Прототипы никак не влияют на this.
Неважно, где находится метод: в объекте или его прототипе. При вызове метода
this— всегда объект перед точкой.
Например, здесь animal представляет собой «хранилище методов», и rabbit использует его.
Вызов rabbit.sleep() устанавливает this.isSleeping для объекта rabbit:
// методы animal
let animal = {
  walk() {
    if (!this.isSleeping) {
      console.log('I walk');
    }
  },
  sleep() {
    this.isSleeping = true;
  }
};
let rabbit = {
  name: 'White Rabbit',
  __proto__: animal,
}
// модифицирует rabbit.isSleeping
rabbit.sleep();
console.log(rabbit.isSleeping); // -> true
console.log(animal.isSleeping); // -> undefined (нет такого свойства в прототипе)
Итого
- В JavaScript все объекты имеют скрытое свойство 
[[Prototype]], которое является либо другим объектом, либоnull. - Мы можем использовать 
obj.__proto__для доступа к[[Prototype]]. - Объект, на который ссылается 
[[Prototype]], называется «прототипом». - Если мы хотим прочитать свойство 
objили вызвать метод, которого не существует уobj, тогда JavaScript попытается найти его в прототипе. - Операции записи/удаления работают непосредственно с объектом, они не используют прототип.
 - Если мы вызываем 
obj.method(), а метод при этом взят из прототипа, тоthisвсе равно ссылается наobj. 
Конспект статьи из учебника по JavaScript — Прототипное наследование