Sobes.tech
Junior

Spiega come funziona l'ereditarietà in JavaScript.

sobes.tech AI

Risposta dell'AI

L'ereditarietà in JavaScript viene implementata tramite prototipi. Ogni oggetto in JavaScript ha una proprietà interna [[Prototype]] che fa riferimento a un altro oggetto. Quando si tenta di accedere a una proprietà o metodo di un oggetto che non è presente direttamente su di esso, il motore JavaScript cerca questa proprietà o metodo nel prototipo di quell'oggetto. Se non lo trova, continua la ricerca lungo la catena di prototipi, che si forma tramite le connessioni [[Prototype]], fino a raggiungere null (fine della catena).

Esistono due meccanismi principali di ereditarietà:

  1. Ereditarietà prototipica classica (ES5 e precedenti): Basata sull'uso di funzioni costruttore e sulla loro proprietà prototype.

    // Funzione costruttore
    function Animal(name) {
      this.name = name;
    }
    
    // Aggiunta di metodo al prototipo
    Animal.prototype.speak = function() {
      console.log(`${this.name} emette un suono.`);
    };
    
    // Creazione di un'istanza
    const dog = new Animal('Cane');
    
    // Chiamata del metodo dal prototipo
    dog.speak(); // Stampa: Cane emette un suono.
    
    // Il prototipo di cat è Animal.prototype
    console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
    
  2. Ereditarietà basata su classi (ES6 e successivi): Usa la sintassi class e extends, che internamente opera comunque con i prototipi.

    // Classe base
    class Animal {
      constructor(name) {
        this.name = name;
      }
    
      speak() {
        console.log(`${this.name} emette un suono.`);
      }
    }
    
    // Sottoclasse che eredita da Animal
    class Dog extends Animal {
      constructor(name, breeds) {
        super(name); // Chiamata al costruttore della classe genitore
        this.breeds = breeds;
      }
    
      bark() {
        console.log(`${this.name} abbaia.`);
      }
    }
    
    // Creazione di un'istanza della sottoclasse
    const myDog = new Dog('Bublik', ['Labrador', 'Collie']);
    
    // Chiamata ai metodi di Animal e Dog
    myDog.speak(); // Stampa: Bublik emette un suono.
    myDog.bark();  // Stampa: Bublik abbaia.
    
    // Il prototipo di myDog è Dog.prototype
    console.log(Object.getPrototypeOf(myDog) === Dog.prototype); // true
    
    // Il prototipo di Dog.prototype è Animal.prototype
    console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
    

Concetti chiave:

  • Catenza di prototipi: La relazione tra oggetti tramite i loro [[Prototype]].

  • __proto__ (obsoleto/non standard, ma utile per capire): Proprietà tramite cui si può accedere a [[Prototype]]. Non si consiglia di usarlo in produzione.

  • Object.getPrototypeOf(obj) : Metodo standard per ottenere il prototipo di un oggetto.

  • Object.setPrototypeOf(obj, proto) : Metodo standard per modificare il prototipo di un oggetto (non si consiglia di modificarlo dopo la creazione di un oggetto esistente).

  • instanceof : Operatore per verificare se un oggetto è istanza di una classe o funzione costruttrice, controllando la sua catena di prototipi.

    console.log(myDog instanceof Dog);   // true
    console.log(myDog instanceof Animal); // true
    console.log(myDog instanceof Object); // true (alla fine della catena Object.prototype)
    
  • super : Usato nelle classi per chiamare metodi o il costruttore della classe genitore.

La differenza tra approccio classico e basato su classi: Le classi offrono un modo più strutturato e sintatticamente comodo di lavorare con l'ereditarietà prototipica, simile ad altri linguaggi di programmazione. Tuttavia, il meccanismo di base rimane lo stesso — ereditarietà tramite prototipi.