Ciężka sprawa. Czym się różni prototype
od __proto__
? Kilka razy podchodziłam do tego tematu, który okazał się być rzeką szeroką i głęboką. Chciałabym więc przedstawić tylko podstawy prototypów, tak w pigułce. Marzy mi się, że trudny temat nie odstraszy potencjalnych czytelników. Zamiast tego zdecydują się poświęcić 5 minut życia, aby być bogatszymi o dawkę konkretnej javascriptowej teorii. Być może w przyszłości, przy pomyślnych wiatrach, dopiszę część drugą artykułu poszerzając podaną tu wiedzę.
prototype
Zacznijmy od tego, że każda funkcja jest obiektem w JS. A więc może posiadać swoje własności czy metody. I właśnie prototype
jest własnością każdej funkcji! Natywną, czyli nie musimy jej sami ustalać. Po prostu każda funkcja automatycznie dostaje własność prototype
.
Własność prototype
nie mówi „oto prototyp tej funkcji”.
Własność prototype
mówi: „oto prototyp obiektu stworzonego przez tę funkcję”.
prototypy
Funkcja może być też użyta jako konstruktor. Dzieje się to wtedy, kiedy za pomocą funkcji oraz słowa new
tworzymy nowy obiekt.
Kiedy tworzysz nowy obiekt, możesz ustawić jego prototyp (czytaj: skąd ma dziedziczyć własności). Kiedy w kodzie odwołujesz się do jakiejś własności obiektu, najpierw jest ona szukana w danym obiekcie. Jeśli nie jest znaleziona, to wtedy JS kopie dalej i szuka w prototypie obiektu. W razie konieczności jeszcze dalej w prototypie prototypu. Aż do skutku. JS ma na to ładne określenie – prototype chain.
W całym procesie podróży po łańcuchu prototypów, możemy ustalić owe prototypy za pomocą pewnej pseudo własności – __proto__
.
__proto__
Wymawiane jako “dunder proto” jako skrót od “double underscore proto”.
Wiemy już, że własność __proto__
jest kluczowa w temacie ustalania łańcucha prototypów. To skąd bierze się jej wartość? Z własności prototype
!
Kiedy tworzymy nowy obiekt za pomocą funkcji konstruktora:
var ChildObject = new ParentObject();
JS szuka w ParentObject
własność prototype
i kopiuje ją do ChildObject
jako… własność __proto__
! NIE jako prototype
.
Bo przecież po co tworzyć rzeczy proste, logiczne i nudne. Życie musi być zaskakujące!
Więc wiemy, że kiedy obiekt jest tworzony funkcją konstruktorem, jego prototypem, czyli też wartością __proto__
jest wartość prototype
funkcji konstruktora.
Przykład:
function Fn() {}
var obj = new Fn();
console.log(obj.__proto__ === Fn.prototype);
// -> true
console.log(obj.__proto__.__proto__=== Object.prototype);
// -> true
console.log(obj.__proto__.__proto__.__proto__ === null);
// -> true
A co z pozostałymi obiektami? Tymi, które nie są stworzone za pomocą funkcji konstruktora? Ich łańcuchy prototypów kończą się zawsze Object.prototype
. Zresztą spójrzcie na przykład:
var obj = {};
var arr = [];
function fn() {}
console.log(obj.__proto__ === Object.prototype);
// -> true
console.log(obj.__proto__.__proto__ === null);
// -> true
console.log(arr.__proto__ === Array.prototype);
// -> true
console.log(arr.__proto__.__proto__ === Object.prototype);
// -> true
console.log(fn.__proto__ === Function.prototype);
// -> true
console.log(fn.__proto__.__proto__ === Object.prototype);
// -> true
Ważna uwaga – nie edytuje się własności __proto__
. Tak z definicji, tak po prostu. To najczarniejsza z czarnych magii. Co prawda __proto__
zostało ustandaryzowane w ES6, jednak może zostać deprecated na korzyść dla Object.getPrototypeOf()
.
Podsumowując.
Własność prototype
jest wykorzystywana do utworzenia własności __proto__
.
Źródła i banki fajnej dodatkowej wiedzy: KLIK!, KLIK!, KLIK!, KLIK!, KLIK!.