Sobes.tech
Junior

Wyjaśnij, jak działa dziedziczenie w JavaScript.

sobes.tech AI

Odpowiedź od AI

Dziedziczenie w JavaScript realizowane jest przez prototypy. Każdy obiekt w JavaScript ma właściwość wewnętrzną [[Prototype]], która odwołuje się do innego obiektu. Gdy próbujemy uzyskać dostęp do właściwości lub metody obiektu, którego nie ma bezpośrednio na nim, silnik JavaScript szuka tej właściwości lub metody w prototypie tego obiektu. Jeśli jej tam nie znajdzie, kontynuuje poszukiwania w łańcuchu prototypów, który jest tworzony przez połączenia [[Prototype]], aż do osiągnięcia null (końca łańcucha).

Istnieją dwa główne mechanizmy dziedziczenia:

  1. Klasyczne dziedziczenie prototypowe (ES5 i wcześniejsze): Oparte na użyciu funkcji konstruktorów i ich właściwości prototype.

    // Funkcja konstruktora
    function Animal(name) {
      this.name = name;
    }
    
    // Dodanie metody do prototypu
    Animal.prototype.speak = function() {
      console.log(`${this.name} wydaje dźwięk.`);
    };
    
    // Tworzenie instancji
    const dog = new Animal('Pies');
    
    // Wywołanie metody z prototypu
    dog.speak(); // Wyświetli: Pies wydaje dźwięk.
    
    // Prototyp cat to Animal.prototype
    console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
    
  2. Dziedziczenie oparte na klasach (ES6 i nowsze): Używa składni class i extends, które wewnętrznie nadal operują na prototypach.

    // Klasa bazowa
    class Animal {
      constructor(name) {
        this.name = name;
      }
    
      speak() {
        console.log(`${this.name} wydaje dźwięk.`);
      }
    }
    
    // Podklasa dziedzicząca po Animal
    class Dog extends Animal {
      constructor(name, breeds) {
        super(name); // Wywołanie konstruktora klasy nadrzędnej
        this.breeds = breeds;
      }
    
      bark() {
        console.log(`${this.name} szczeka.`);
      }
    }
    
    // Tworzymy instancję podklasy
    const myDog = new Dog('Bublik', ['Labrador', 'Collie']);
    
    // Wywołujemy metody zarówno z Animal, jak i z Dog
    myDog.speak(); // Wyświetli: Bublik wydaje dźwięk.
    myDog.bark();  // Wyświetli: Bublik szczeka.
    
    // Prototyp myDog to Dog.prototype
    console.log(Object.getPrototypeOf(myDog) === Dog.prototype); // true
    
    // Prototyp Dog.prototype to Animal.prototype
    console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
    

Kluczowe koncepcje:

  • Łańcuch prototypów: Związek między obiektami przez ich [[Prototype]].

  • __proto__ (przestarzałe/niestandardowe, ale pomocne do zrozumienia): Właściwość, przez którą można uzyskać dostęp do [[Prototype]]. Nie zaleca się używania w produkcji.

  • Object.getPrototypeOf(obj) : Standardowy sposób uzyskania prototypu obiektu.

  • Object.setPrototypeOf(obj, proto) : Standardowy sposób zmiany prototypu obiektu (niezalecane do zmiany prototypu istniejących obiektów po ich utworzeniu).

  • instanceof : Operator do sprawdzania, czy obiekt jest instancją klasy lub funkcji konstrukcyjnej, poprzez sprawdzenie jego łańcucha prototypów.

    console.log(myDog instanceof Dog);   // true
    console.log(myDog instanceof Animal); // true
    console.log(myDog instanceof Object); // true (na końcu łańcucha Object.prototype)
    
  • super : Używany w klasach do wywoływania metod lub konstruktora klasy nadrzędnej.

Różnica między podejściem klasycznym a opartym na klasach: Klasy zapewniają bardziej strukturalny i składniowo wygodny sposób pracy z dziedziczeniem prototypowym, zbliżony do innych języków programowania. Jednak podstawowy mechanizm pozostaje ten sam — dziedziczenie przez prototypy.