Sobes.tech
Назад к вопросам
Junior
90
questionbank

Объясните, как работает наследование в JavaScript.

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Наследование в JavaScript реализуется через прототипы. Каждый объект в JavaScript имеет внутреннее свойство [[Prototype]], которое ссылается на другой объект. Когда мы пытаемся получить доступ к свойству или методу объекта, которого нет непосредственно у него, движок JavaScript ищет это свойство или метод в прототипе этого объекта. Если он не находит его там, то продолжает поиск по цепочке прототипов, которая образуется связями [[Prototype]], пока не достигнет null (конца цепочки).

Существует два основных механизма наследования:

  1. Классическое прототипное наследование (ES5 и ранее): Основано на использовании функций-конструкторов и их свойства prototype.

    // Функция-конструктор
    function Animal(name) {
      this.name = name;
    }
    
    // Добавляем метод в прототип
    Animal.prototype.speak = function() {
      console.log(`${this.name} издает звук.`);
    };
    
    // Создаем экземпляр
    const dog = new Animal('Собака');
    
    // Вызываем метод из прототипа
    dog.speak(); // Выведет: Собака издает звук.
    
    // Прототип cat - это Animal.prototype
    console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
    
  2. Классовое наследование (ES6 и позднее): Использует синтаксический сахар class и extends, который под капотом всё равно оперирует прототипами.

    // Базовый класс
    class Animal {
      constructor(name) {
        this.name = name;
      }
    
      speak() {
        console.log(`${this.name} издает звук.`);
      }
    }
    
    // Подкласс, наследующий от Animal
    class Dog extends Animal {
      constructor(name, breeds) {
        super(name); // Вызов конструктора родительского класса
        this.breeds = breeds;
      }
    
      bark() {
        console.log(`${this.name} лает.`);
      }
    }
    
    // Создаем экземпляр подкласса
    const myDog = new Dog('Бублик', ['Лабрадор', 'Колли']);
    
    // Вызываем методы как из Animal, так и из Dog
    myDog.speak(); // Выведет: Бублик издает звук.
    myDog.bark();  // Выведет: Бублик лает.
    
    // Прототип myDog - это Dog.prototype
    console.log(Object.getPrototypeOf(myDog) === Dog.prototype); // true
    
    // Прототип Dog.prototype - это Animal.prototype
    console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
    

Ключевые концепции:

  • Прототипная цепочка: Связь между объектами через их [[Prototype]].

  • __proto__ (устаревший/нестандартный, но полезный для понимания): Свойство, через которое можно получить доступ к [[Prototype]]. Не рекомендуется использовать в продакшн-коде.

  • Object.getPrototypeOf(obj): Стандартный способ получить прототип объекта.

  • Object.setPrototypeOf(obj, proto): Стандартный способ изменить прототип объекта (не рекомендуется для изменения прототипа в уже существующих объектах после их создания).

  • instanceof: Оператор для проверки, является ли объект экземпляром класса или функции-конструктора, путём проверки его прототипной цепочки.

    console.log(myDog instanceof Dog);   // true
    console.log(myDog instanceof Animal); // true
    console.log(myDog instanceof Object); // true (в конце цепочки Object.prototype)
    
  • super: Используется в классах для вызова методов или конструктора родительского класса.

Разница между классическим и классовым подходами: Классы предоставляют более структурированный и синтаксически удобный способ работы с прототипным наследованием, приближенный к другим языкам программирования. Однако фундаментальный механизм остаётся тем же — наследование через прототипы.