Sobes.tech
Junior

Vysvětlete, jak funguje dědičnost v JavaScriptu.

sobes.tech AI

Odpověď od AI

Dědičnost v JavaScriptu je realizována přes prototypy. Každý objekt v JavaScriptu má vnitřní vlastnost [[Prototype]], která odkazuje na jiný objekt. Když se pokusíme získat přístup k vlastnosti nebo metodě objektu, která přímo u něj není, JavaScript engine hledá tuto vlastnost nebo metodu v prototypu tohoto objektu. Pokud ji nenajde, pokračuje v hledání po řetězci prototypů, který je tvořen spojeními [[Prototype]], dokud nedosáhne null (konec řetězce).

Existují dva hlavní mechanismy dědičnosti:

  1. Klasická prototypová dědičnost (ES5 a dříve): Je založena na použití konstrukčních funkcí a jejich vlastnosti prototype.

    // Konstrukční funkce
    function Zvíře(jméno) {
      this.jméno = jméno;
    }
    
    // Přidání metody do prototypu
    Zvíře.prototype.mluví = function() {
      console.log(`${this.jméno} vydává zvuk.`);
    };
    
    // Vytvoření instance
    const pes = new Zvíře('Pes');
    
    // Volání metody z prototypu
    pes.mluví(); // Výstup: Pes vydává zvuk.
    
    // Prototyp `dog` je `Zvíře.prototype`
    console.log(Object.getPrototypeOf(pes) === Zvíře.prototype); // true
    
  2. Třída (ES6 a novější): Používá syntaktický cukr class a extends, který pod kapotou stále pracuje s prototypy.

    // Základní třída
    class Zvíře {
      constructor(jméno) {
        this.jméno = jméno;
      }
    
      mluví() {
        console.log(`${this.jméno} vydává zvuk.`);
      }
    }
    
    // Podtřída, dědí z Zvíře
    class Pes extends Zvíře {
      constructor(jméno, plemena) {
        super(jméno); // Volání konstruktoru rodičovské třídy
        this.plemena = plemena;
      }
    
      štěká() {
        console.log(`${this.jméno} štěká.`);
      }
    }
    
    // Vytvoření instance podtřídy
    const můjPes = new Pes('Bublik', ['Labrador', 'Collie']);
    
    // Volání metod jak z Zvíře, tak z Pes
    můjPes.mluví(); // Výstup: Bublik vydává zvuk.
    můjPes.stěká();  // Výstup: Bublik štěká.
    
    // Prototyp `Pes.prototype` je `Zvíře.prototype`
    console.log(Object.getPrototypeOf(můjPes) === Pes.prototype); // true
    
    // Prototyp `Pes.prototype` je `Zvíře.prototype`
    console.log(Object.getPrototypeOf(Pes.prototype) === Zvíře.prototype); // true
    

Klíčové koncepty:

  • Řetězec prototypů: Spojení mezi objekty přes jejich [[Prototype]].

  • __proto__ (zastaralé/nestandardní, ale užitečné pro pochopení): Vlastnost, přes kterou lze získat přístup k [[Prototype]]. Nedoporučuje se používat v produkčním kódu.

  • Object.getPrototypeOf(obj): Standardní způsob získání prototypu objektu.

  • Object.setPrototypeOf(obj, proto): Standardní způsob změny prototypu objektu (nedoporučuje se měnit prototypy u již existujících objektů po jejich vytvoření).

  • instanceof: Operátor pro kontrolu, zda je objekt instancí třídy nebo konstrukční funkce, prostřednictvím kontroly jeho řetězce prototypů.

    console.log(můjPes instanceof Pes);   // true
    console.log(můjPes instanceof Zvíře); // true
    console.log(můjPes instanceof Object); // true (na konci řetězce je `Object.prototype`)
    
  • super: Používá se v třídách k volání metod nebo konstruktoru rodičovské třídy.

Rozdíl mezi klasickým a třídním přístupem: Třídy poskytují strukturovanější a syntakticky pohodlnější způsob práce s prototypovým děděním, podobně jako v jiných programovacích jazycích. Nicméně základní mechanismus zůstává stejný — dědění přes prototypy.