Prototypy w JavaScript – wstęp

Javascript nie ma klas jak inne języki programowania, ale ma prototypy, które umożliwiają dziedziczenie. Zamiast dziedziczenia na bazie klas mamy dziedziczenie prototypowe.

Dziedziczenie to współdzielenie pewnych funkcjonalności np. metod z innych obiektów.

Wszystko w JavaScript oprócz typów prostych jest obiektem, nawet funkcje.  Każda funkcja posiada również metody np. call oraz apply oraz właściwość prototype.

Domyślnie prototyp jest pustym obiektem stworzonym podczas definicji funkcji.

Każda instancja stworzona za pomocą tego samego konstruktora będzie współdzieliła ten sam prototyp.

Popatrzmy na zwykłą funkcję thing:

function thing() {
}

thing.prototype; // każda funkcja ma właściwości np prototype
thing.call(); // dodatkowo jest dostęp do funkcji np. call etc
thing.apply()
thing.toString();

Jeśli do obiektu prototype dodamy pola i metody to stworzone nowe obiekty  z konstruktora będą miały dostęp do tych właściwości.

function Thing(name) { // konstruktor Thing
    this.name = name; 
}

Thing.prototype.year = 2000;
Thing.prototype.printInfo = function() {
    console.log(this.name, this.year);
}

let telephone = new Thing("phone");
telephone.printInfo(); // phone 2000

let tv = new Thing("tv");
tv.printInfo(); // tv 2000

Jeśli dodamy coś do prototype to będzie widoczne we wszystkich instancjach, nawet po ich utworzeniu.

Thing.prototype.data = { str: "text" };
Thing.prototype.weight = 50;

// obie instancje telephone i tv mają dostęp do tych samych pól, 
console.log( telephone.weight ); // 50
console.log( tv.weight ); // 50

console.log( telephone.data.str ); // text
console.log( tv.data.str  ); // text

// pola oraz metody z prototype nie są kopiami w nowych obiektach, to referencje
telephone.data === tv.data; // true

 

Jak działa mechanizm prototypów w JavaScript:

function Thing(name) { 
    this.name = name; 
}

Thing.prototype.weight = 50;
let telephone = new Thing("phone");
console.log( telephone.weight ); // 50

– Interpreter JavaScript spotyka odwołanie do właściwości weight.
– sprawdza czy weight istnieje w telephone, jeśli tak to zostanie użyta wartość z tego obiektu
– telephone nie ma weight, dlatego interpreter poszuka jej w prototype, który jest w konstruktorze tego obiektu i użyje tej wartości:

telephone.constructor.prototype.weight; // 50

Uwaga, jeśli w prototypie nie byłoby weight to będzie szukana dalej w łańcuchu prototypów, ponieważ prototype też jest obiektem, również ma konstruktor, która ma swój prototype. Przeszukany będzie łańcuch prototypów, aż interpreter dotrze do wbudowanego obiektu Object, który jest rodzicem wszystkich obiektów.

// łańcuch prototypów
telephone.constructor.prototype.constructor.prototype  

Przesłanianie pól prototype jest możliwe definiując właściwość dla utworzonego obiektu:

function Thing(name) { 
    this.name = name; 
}

Thing.prototype.weight = 50;
let telephone = new Thing("phone");
console.log( telephone.weight );// 50 z prototypu



// przesłonięcie weight z prototypu bezpośrednią 
// właściwością weight w instancji
telephone.weight = 3;
console.log( telephone.weight ); // 3

Na dziś to tyle 🙂

KW

Scroll to Top