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 Ζώο('Σκύλος');
    
    // Κλήση μεθόδου από το πρωτότυπο
    σκύλος.μιλάει(); // Έξοδος: Σκύλος κάνει ήχο.
    
    // Το πρωτότυπο του σκύλου είναι το `Ζώο.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`
    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: Χρησιμοποιείται σε κλάσεις για την κλήση μεθόδων ή του κατασκευαστή της γονικής κλάσης.

Η διαφορά μεταξύ κλασικής και κλασικής προσέγγισης: Οι κλάσεις παρέχουν έναν πιο δομημένο και συντακτικά άνετο τρόπο εργασίας με την πρωτοτυπική κληρονομιά, κοντά σε άλλες γλώσσες προγραμματισμού. Ωστόσο, ο βασικός μηχανισμός παραμένει ο ίδιος — η κληρονομιά μέσω πρωτοτύπων.