Od 2015 roku dzięki specyfikacji ES6 a.k.a. ES2015 mamy możliwość stworzenia klasy jako reprezentacji opisu obiektu, na podstawie której zostanie on stworzony.
Co jeśli architektura Twojej aplikacji nie opiera się na klasach?
A może po prostu nie jesteś zwolennikiem Object-Oriented Programming (OOP)?
Taka sytuacja jest dosyć częsta, kiedy zaczynasz poznawać świat programowania w JavaScript i klasy nie są jeszcze Ci znane.
Z pomocą przychodzi wzorzec delegacji i styl Objects Linked to Other
Objects (OLOO), który swoją definicję posiada w książce “You Don’t Know JS”
autorstwa Kyle Simsona.
Dziedziczenie
Przykładowy model architektury w ujęciu klasycznym, czyli istnieją obiekty główne (rodzice) oraz obiekty pochodne (dzieci).
Dziedziczenie obiektów? Nie!
Model architektury opartej na OLOO nie wprowadza hierarchii. On po prostu informuje, że jeden obiekty będą korzystały z innych.
Tego procesu nie można nazwać dziedziczeniem, tylko delegacją.
W specyfikacji ES2015 została zdefiniowana funkcja statyczna
Object.setPrototypeOf, która łączy ze sobą obiekty, ustawiając ich
wewnętrzne wiązanie [[Prototype]] (dostępne też przez akcesor __proto__).
Takie wiązanie ma każdy obiekt, także funkcja. Właściwość prototype, którą
mają funkcje, to coś innego: to zwykła właściwość konstruktora, z której
new bierze prototyp dla tworzonych obiektów.
const point = {
x: null,
y: null,
draw() {
// ...
}
};
const point1 = {
x: 0,
y: 0
};
console.log(typeof point1.draw); // undefined
Object.setPrototypeOf(point1, point);
console.log(point1.hasOwnProperty('draw')); // false
console.log('draw' in point1); // true
console.log(typeof point1.draw); // "function"
const point2 = {
x: 50,
y: 50
};
Object.setPrototypeOf(point2, point);
Uwaga
Zmiana prototypu istniejącego obiektu przez
Object.setPrototypeOfjest według MDN bardzo wolna i psuje optymalizacje silnika JavaScript. Lepiej od razu tworzyć obiekt z wybranym prototypem:const point1 = Object.assign(Object.create(point), { x: 0, y: 0 });
Weryfikacja
Aby móc zweryfikować aktualny stan proponuję uruchomić:
// Check: Different points
console.log(point1 !== point2) // true
// Same definition of "draw" method
console.log(point.draw === point1.draw) // true
console.log(point.draw === point2.draw) // true
console.log(point1.draw === point2.draw) // true
Wsparcie systemowe
Ale gdzie można używać takiej składni zapytasz? Otóż dzięki najpopularniejszemu zestawieniu wsparcia feature-ów JavaScript-owych, realizowanemu przez Kangax-a, możemy się dowiedzieć, że:
Jest wsparcie
Poniższe wersje były aktualne w dniu pisania - minimalne są dużo niższe (np. Chrome 34, Firefox 31, Safari 9, Edge 12).
- Google Chrome 57+ (Opera 44+)
- Firefox 52+
- Safari:
- desktop: 10+
- mobile: 9+
- Microsoft
- IE 11
- Edge 14+
- Node.js 4+
Brak wsparcia
Sam transpiler nie pomoże w zapewnieniu wsparcia funkcji
Object.setPrototypeOf, bo to wbudowana funkcja, a nie składnia - potrzebny
jest polyfill (np. z biblioteki core-js). W zestawieniu brak wsparcia mają:
- Babel.js
- Traceur
- Closure
- TypeScript
Link do pełnego zestawienia.
Polyfill
Link do definicji polyfilla znajduje się na MDN jednak, abyś mógł szybko zobaczyć jak on wygląda umieszczę go tutaj:
// Only works in Chrome and FireFox, does not work in IE 10 and older:
Object.setPrototypeOf = Object.setPrototypeOf || function(obj, proto) {
obj.__proto__ = proto;
return obj;
}
Cya! 👋