Sobes.tech
Junior

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

sobes.tech AI

Отговор от AI

Наследяването в JavaScript се реализира чрез прототипи. Всеки обект в JavaScript има вътрешно свойство [[Prototype]], което сочи към друг обект. Когато се опитваме да достъпим свойство или метод на обект, който не е директно при него, JavaScript движката търси това свойство или метод в прототипа на този обект. Ако не го намери там, продължава търсенето по веригата от прототипи, която се образува чрез връзките [[Prototype]], докато достигне null (края на веригата).

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

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

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

    // Основен клас
    class Животно {
      constructor(име) {
        this.име = име;
      }
    
      говори() {
        console.log(`${this.име} издава звук.`);
      }
    }
    
    // Подклас, наследяващ от Животно
    class Куче extends Животно {
      constructor(име, породи) {
        super(име); // Вика конструктора на родителския клас
        this.породи = породи;
      }
    
      лае() {
        console.log(`${this.име} лае.`);
      }
    }
    
    // Създаване на екземпляр от подклас
    const моетоКуче = new Куче('Bublik', ['Labrador', 'Collie']);
    
    // Викаме методи както от Животно, така и от Куче
    моетоКуче.говори(); // Изход: Bublik издава звук.
    моетоКуче.лае();  // Изход: Bublik лае.
    
    // Прототипът `Куче.prototype` е `Животно.prototype`
    console.log(Object.getPrototypeOf(моетоКуче) === Куче.prototype); // true
    
    // Прототипът `Куче.prototype` е `Животно.prototype`
    console.log(Object.getPrototypeOf(Куче.prototype) === Животно.prototype); // true
    

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

  • Веригата от прототипи: Връзката между обектите чрез техните [[Prototype]].

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

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

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

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

    console.log(моетоКуче instanceof Куче);   // true
    console.log(моетоКуче instanceof Животно); // true
    console.log(моетоКуче instanceof Object); // true (в края на веригата е `Object.prototype`)
    
  • super: Използва се в класове за извикване на методи или конструктора на родителския клас.

Разликата между класическото и класовото подходи: Класовете предоставят по-структуриран и синтактично удобен начин за работа с прототипното наследяване, подобно на други програмни езици. Въпреки това, основният механизъм остава същият — наследяването чрез прототипи.