Programowanie obiektowe jest to styl pisania kodu umożliwiający bardzo efektywne i szybkie tworzenie programów. Pozwalam w prosty sposób zapanować nad kodem, w jego utrzymaniu oraz rozwijaniu. Programowanie prototypowe jest stylem programowania obiektowego, który odbiega od klasycznej obiektowości znanej z innych języków programowania typu java, php, c++. Przy użyciu prototypów. Jest programowaniem “bezklasowym”, nie występuje klasyczne słowo “class” ponieważ klasy są nieobecne.

Prototypy są elementami funkcji dzięki którym w wygodny sposób możemy zdefiniować właściwości i funkcjonalości które automatycznie będą stosowane do instancji obiektów. Wszystkie funkcje zawierają właściwości prototype które odwołują się do pustego obiektu. Zamiast klas w javascript stosuje się funkcje, możemy zdefiniować je na 2 sposoby:

function Bird() { }
// or
var Mammal = function() { }

// checking the type
console.log(typeof Mammal, typeof Bird); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/tupewo/1/embed?js,console”>JS
Bin</a><script src=”​http://static.jsbin.com/js/embed.js„></script>​

Z powyższego kodu wynika (1) iż funkcje nie są obiektami aż do momentu utworzenia instancji, do jej stworzenia posłuży nam kluczowe słowo new , poniższy przykład tworzy instancje dla Mammal oraz Bird.

var obj1 = new Mammal();
var obj2 = new Bird();

// checking the type
console.log(typeof obj1, typeof obj2); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/yinoze/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

Utworzyliśmy instancje obiektów, po sprawdzeniu typów (2) wszystko się zgadza, wyświetli object, object. Fatalnym w skutkach błędem było by pominięcie wyrażenia new skutkowało by to zanieczyszczeniem przestrzeni globalnej oraz błędem skryptu który przerwał by stos przetwarzanych poleceń. Metody zdefiniowane przez prototypy były by po prostu niedostępne, przykład:

function Bird() {}
Bird.prototype.fly = function() {
return true;
}

var obj1 = Bird();
console.log(obj1); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/xenigo/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

zmienna obj1 nie jest obiektem lecz undefined (1) czyli nie posiada metody fly(), inaczej wygląda sytuacja w momencie utworzenia instancji, poprzez użycie wyrażenia new:

function Bird() {}
Bird.prototype.fly = function() {

return true;
}

var obj1 = new Bird();
console.log(obj1 && obj1.fly()); // (1)

<a class=”jsbin­embed” href=”http://jsbin.com/seviso/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

Jak pokazuje powyższy przykład, próba odwołania do metody zwraca (1) true; Podczas tworzenia instancji możemy przesłać do niej pewne właściwości za pomocą konstruktora. Konstruktor jest wywoływany w momencie inicjalizacji obiektu. Właściwości powinny być ustawione we właściwości prototype funkcji, co umożliwi nam późniejsze poprawne dziedziczenie

function Bird(color) {
this.color = color;

}

var obj = new Bird(“yellow”);
console.log(obj.color); // (1)

<a class=”jsbin­embed”href=”http://jsbin.com/mivelu/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

dostęp do pól, metod wewnątrz obiektu odbywa się za pomocą kluczowego słowa this które odnosi się do aktualnego obiektu, z poza obiektu poprzez składnie: nazwa_instancji.właściwość (1). Metody definiuje się jako funkcje, mogą zawierać argumenty, przypisujemy jako tak samo jak właściwości, do obiektu prototype.

function Bird(color) {
this.color = color;
}

Bird.prototype.info = function(weight, size) {
this.weight = weight;
this.size = size;

console.log(this.color + ” bird, weight ” + this.weight + ” and ” + this.size + ” size”);

var obj1 = new Bird(„yellow”);
var obj2 = new Bird(„green”);

obj1.info(1, 2); // 1 „yellow bird, weight 1 and 2 size”

var fn = obj2.info;

fn(1, 2); // 2 „undefined bird, weight 1 and 2 size”

fn.call(obj2, 1, 2); // 3 „green bird, weight 1 and 2 size”
fn.apply(obj2, [1, 2]); // 4 „green bird, weight 1 and 2 size”

<a class=”jsbin­embed” href=”http://jsbin.com/rusetes/1/embed?js,console”>JS
Bin</a><script src=”​ http://static.jsbin.com/js/embed.js„></script>​

W powyższym przykładzie (1) odwołujemy się to metody info poprzez instancje obj1 co w consoli wyświetla napis „yellow bird, weight 1 and 2 size”, w pkt (2) powinno być “green bird, weight 1 and 2 size” jednakże tak się nie dzieje. Problem polega na fakcie iż przypisaliśmy funkcje do zmiennej która należy obiektu globalnego window, który nie posiada w swojej przestrzeni właściwości color, żeby rozwiązać problem posłuży na fakt iż metody są funkcjami przypisanymi do obiektu jako właściwości oraz znajomość jednej funkcji call lub apply pozwalają one ustawić metodę innego obiektu w kontekście innego, (3) metoda call przyjmuje kolejno argumenty: kontekst, arg1, arg2… (argument jeden po drugim), zaś metoda apply (4) posiada 2 argumenty: kontekst, tablica argumentów. Dzięki czemu skrypt wyświetlił prawidłową wiadomość.

Zajmijmy się teraz dziedziczeniem, w programowaniu prototypowym istnieje jedna relacja, dziecko ­ rodzic, dziecko jest klasą wyspecjalizowaną oraz dziedziczy metody i własności od rodzica. W poniższym przykładzie definiujemy obiekt rodzica Animal oraz 2 obiekty potomne dzieci, Bird oraz Mammal.

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

Animal.prototype.eat = function() {
console.log(this.name + ” eats.”);

Animal.prototype.sleep = function() {
console.log(this.name + ” sleeps.”);

function Bird(name, color) {
Animal.call(this, name); // 1
}

Bird.prototype = Object.create(Animal.prototype); // 2
Bird.prototype.constructor = Bird; // 3

Bird.prototype.fly = function() {
console.log(this.name + ” flies.”);

function Mammal(name) {
Animal.call(this, name); // 1
}

Mammal.prototype = Object.create(Animal.prototype); // 2
Mammal.prototype.constructor = Mammal; // 3

Mammal.prototype.run = function() {
console.log(this.name + ” runs.”);

var mammal = new Mammal(“dog”);
var bird = new Bird(“parrot”);

mammal.eat(); // “dog eats.” (4)
mammal.run(); // “dog runs.” (4)

bird.eat(); // “parrot eats.” (4)
bird.fly(); // “parrot flies.” (4)

console.log(bird instanceof Animal); // true (5)
console.log(mammal instanceof Animal); // true (5)

<a href=”http://jsbin.com/kefiwa/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

Analizując powyższy kod możemy zauważyć że w konstruktorze dziecka jest wywoływany konstruktor rodzica (1) w kontekście dziecka. Umożliwia nam to dostęp to wszystkich metod obiektu Animal. (2) tworzymy obiekt dziecka za pomocą Object.create którego celem jest stworzenie obiektu o określonych właściwościach, w naszym przypadku jest to Animal. (3) Ustawiamy konstruktor. (4) Odwołujemy się do metod wyspecjalizowanych dziecka jak i do metod rodzica. (5) Sprawdzamy czy obiekt dziecka jest instancją obiektu rodzica.

Warto wspomnieć iż JavaScript posiada obiekty “wbudowane” tj Math, Object, Array, i String. Przy próbie wyspecjalizowania tych obiektów powinno się zachować szczególną ostrożność, ponieważ będą one dodane do wszystkich obiektów danego typu, przykładowo podczas rozszerzania Object.prototype możemy spowodować nie oczekiwane błędy, przykład:

Object.prototype.items = function() {
var items = [];
for (var p in this)
items.push(p); return items;

var obj = { a: 1, b: 2, c: 3 };
console.log(obj.items()); // [„a”, „b”, „c”, „items”]

<a class=”jsbin­embed” href=”http://jsbin.com/newopa/1/embed?js,console”>JS
Bin</a><script src=”http://static.jsbin.com/js/embed.js”></script>

Wydaje się że powinno zwrócić 3 elementy, zwraca 4, aby zapobiec temu podczas iteracji należało by sprawdzić czy obiekt ma określone właściwości za pomocą metody hasOwnProperty().

Poprzez dziedziczenie odkrywamy mechanizm abstrakcji, którego zadaniem jest utworzenie wyspecjalizowanych obiektów klas rodziców lub kompozycje czyli instacja innego obiektu będzie zawarta w właściwościach innej klasy. Między klasami dziedziczonymi zachodzi też polimorfizm czyli klasa dziecka jest tego samego typu co klasa rodzica. Polimorfizm działa tylko w linii dziecko ­ rodzic w jedną strone, w drugim kierunku nie zachodzi. Przykład:

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

function Bird(name, color) {
Animal.call(this, name);
}

Bird.prototype = Object.create(Animal.prototype);

Bird.prototype.constructor = Bird;

var bird = new Bird(“parrot”);

console.log(bird instanceof Object); // (1)
console.log(bird instanceof Animal); // (2)
console.log(bird instanceof Bird); // (3)

<a class=”jsbin­embed” href=”http://jsbin.com/cujawa/1/embed?js,console”>JS
Bin</a><script src=”​ http://static.jsbin.com/js/embed.js„></script>​

Jak widać bird jest instacją klasy Object (1), Animal(2) oraz Bird(3), to jest właśnie poliformizm

 

Chcesz kontrolować, utrzymywać i rozwijać kod razem z Divante? Napisz do nas.