Laboratorium 1, Na czym polega chaining w JavaScript

background image

Na czym polega chaining w JavaScript?

Implementacja kalkulatora oraz własnej biblioteki DOM, które działałyby na zasadzie chainingu,

czyli uruchamianiu metod po metodzie na danym obiekcie, po kropce, podobnie jak w jQuery.

W przypadku kalkulatora mogłoby to wyglądać np. tak:

czysty tekst

1.

calculator.add(2).multiply(3).subtract(3).getResult(); // 3

Działania wykonywane byłyby od lewej, nie zwracalibyśmy również uwagi na kolejność (a więc mnożenie

byłoby równe dodawaniu). Zatem najpierw dodajemy do zera dwójkę, potem mnożymy to przez trzy, a na

końcu odejmujemy trójkę. Wynikiem jest… trzy.

W wypadku biblioteczki DOMowej, przykładowe jej użycie mogłoby wyglądać następująco:

czysty tekst

1.

$("div > p").css("margin-left", "20px").click(function() {

2.

console.log("klik!");

3.

});

Skupmy się jednak na kalkulatorze.

Przed przystąpieniem do implementacji zazwyczaj pojawia się wiele wątpliwości. Po pierwsze należy

zauważyć, analizując nasz przykład, że najpierw uruchamia się funkcja

add

, potem

multiply

,

następnie

subtract

, a na końcu

getResult

. Wszystko dzieje się po kropce.

Warto więc przypomnieć sobie, co oznacza konstrukcja

foo.bar()

, czyli dwie części kodu oddzielone

kropką. Otóż używając takiego kawałka kodu spodziewamy się, że bar jest funkcją (w końcu mamy do

czynienia z nawiasami, więc zapewne chcemy uruchomić jakąś funkcję), a fooobiektem. Otóż tylko wtedy

taka konstrukcja ma sens – po prawej stronie kropki powinna być funkcja, a po lewej obiekt zawierający

taką funkcję. Kod mógłby wyglądać więc tak:

czysty tekst

1.

var foo = { bar: function() { return "test"; } };

2.

foo.bar(); // test

W naszym z kolei przypadku mamy kilka uruchomień funkcji po kropce. Zgadujemy więc, że to co będzie

z lewej musi zwracać jakiś obiekt, zawierający metodę z prawej. A więc po

odpaleniu

calculator.add(2)

spodziewamy się, że

calculator

będzie obiektem zawierającym

metodę

add

. Następnie po uruchomieniu

calculator.add(2).multiply(3)

chcemy, by to, co

zwróci wywołanie

calculator.add(2)

, zawierało metodę

multiply

, ponieważ JS napotkał kropkę,

co znaczy, że z lewej ma znaleźć się obiekt, a z prawej funkcja bądź inna własność z tego obiektu. I tak

dalej. A więc każde wywołanie funkcji kończące się kropką ma zwrócić jakiś obiekt, by kod mógł

zadziałać.

background image

Rozpocznijmy implementację:

czysty tekst

1.

var calculator = {};

Tworzymy obiekt calculator, od którego wszystko się zacznie. Taki obiekt ma metodę add, w końcu nasz

przykład wygląda tak:

czysty tekst

1.

calculator.add(2).multiply(3).subtract(3).getResult(); // 3

Piszemy!

czysty tekst

1.

var calculator = {

2.

add: function(n) {

3.

}

4.

};

Nic prostszego, prawda?

Używamy tego tak:

czysty tekst

1.

calculator.add(2);

Ale my chcemy mieć więcej możliwości, chcemy teraz pomnożyć wszystko przez trzy:

czysty tekst

1.

calculator.add(2).multiply(3)

Wykorzystując obecny, skromny kod, dostajemy błąd. Nic dziwnego – to, co zwraca metoda

add

, nie

zawiera funkcji

multiply

. Naprawmy nasz błąd:

czysty tekst

1.

var calculator = {

2.

add: function(n) {

3.

return {

4.

multiply: function(n2) {

5.

}

6.

};

7.

}

8.

};

Wszystko działa, tylko co, gdyby po add nie było multiply, a na przykład odejmowanie lub kolejne

dodawanie? Pisalibyśmy takie podobiekty w nieskończoność. Zdefiniujmy więc najpierw proste API, które

będzie implementował kalkulator.

czysty tekst

1.

var calculator = {

background image

2.

add: function() {},

3.

subtract: function() {},

4.

multiply: function() {},

5.

divide: function() {}

6.

};

Wszystko fajnie. Brakuje tylko miejsca na przechowywanie wyniku:

czysty tekst

1.

var calculator = {

2.

add: function() {},

3.

subtract: function() {},

4.

multiply: function() {},

5.

divide: function() {},

6.

result: 0

7.

};

Aktualny rezultat naszych operacji składowany jest w

calculator.result

. Początkowa jego wartość

to 0. Uzupełnijmy teraz metody:

czysty tekst

1.

var calculator = {

2.

add: function(n) {

3.

if (typeof n === "number") {

4.

calculator.result += n;

5.

}

6.

},

7.

subtract: function(n) {

8.

if (typeof n === "number") {

9.

calculator.result -= n;

10.

}

11.

},

12.

multiply: function(n) {

13.

if (typeof n === "number") {

14.

calculator.result *= n;

15.

}

16.

},

17.

divide: function(n) {

18.

if (typeof n === "number" && n !== 0) {

19.

calculator.result /= n;

20.

}

21.

},

22.

getResult: function() { return calculator.result; },

23.

result: 0

24.

};

W każdej z nich sprawdzamy, czy podana liczba jest rzeczywiście typy liczbowego (

typeof number

).

Dodatkowo w dzieleniu

divide

nie chcemy dzielić przez zero.

background image

Wszystko pięknie, tylko nadal nie działa nam zapis metod po kropce. Jak wiemy, aby uruchomić metodę

poprzedzoną kropką, musi być ona składnikiem obiektu z lewej strony. A więc każda z naszych funkcji

musi zwracać obiekt z tymi czteroma metodami.

czysty tekst

1.

var calculator = {

2.

add: function(n) {

3.

if (typeof n === "number") {

4.

calculator.result += n;

5.

}

6.

return calculator;

7.

},

8.

subtract: function(n) {

9.

if (typeof n === "number") {

10.

calculator.result -= n;

11.

}

12.

return calculator;

13.

},

14.

multiply: function(n) {

15.

if (typeof n === "number") {

16.

calculator.result *= n;

17.

}

18.

return calculator;

19.

},

20.

divide: function(n) {

21.

if (typeof n === "number" && n !== 0) {

22.

calculator.result /= n;

23.

}

24.

return calculator;

25.

},

26.

getResult: function() { return calculator.result; },

27.

result: 0

28.

};

Jak łatwo zauważyć, każda z nich zwraca obiekt, do którego należy – a więc możemy być pewni, że będzie

zawierał on wszystkie z interesujących nas funkcji, w końcu zwracamy cały kalkulator.

Pozostaje jednak jedna drażniąca kwestia. Otóż możemy łatwo ingerować w wynik – na przykład pisząc

tak:

czysty tekst

1.

calculator.add(2).add(3);

2.

calculator.result = 10;

3.

calculator.getResult(); // 10

Byłoby bardzo miło ukryć dostęp do zmiennej przechowującej tymczasowy wynik. Możemy to zrobić

korzystając z closures i wywołania funkcji anonimowej.

background image

czysty tekst

1.

var calculator = (function() {

2.

var result = 0;

3.

var calculator = {

4.

add: function(n) {

5.

if (typeof n === "number") {

6.

result += n;

7.

}

8.

return calculator;

9.

},

10.

subtract: function(n) {

11.

if (typeof n === "number") {

12.

result -= n;

13.

}

14.

return calculator;

15.

},

16.

multiply: function(n) {

17.

if (typeof n === "number") {

18.

result *= n;

19.

}

20.

return calculator;

21.

},

22.

divide: function(n) {

23.

if (typeof n === "number" && n !== 0) {

24.

result /= n;

25.

}

26.

return calculator;

27.

},

28.

getResult: function() { return result; }

29.

};

30.

31.

return calculator;

32.

})();

Z obiektu

calculator

wydzieliliśmy

result

do oddzielnej zmiennej o tej samej nazwie. Jest ona

dostępna w scope anonimowej funkcji, a więc tylko wewnątrz:

czysty tekst

1.

var calculator = (function() {

2.

// o, tutaj

3.

})();

Wszystkie inne funkcje zdefiniowane wewnątrz niej będą miały do niej dostęp – tak działa mechanizm

closures.

Voila! Podany przykład można by rozszerzyć o

.prototype

i tak dalej, co mogłoby wspierać

dopisywanie pluginów. Można też dodać obsługę błędów. To jednak temat na następny artykuł.

background image

Równie dobrze można skorzystać z this:

czysty tekst

1.

var calculator = (function() {

2.

var result = 0;

3.

var calculator = {

4.

add: function(n) {

5.

if (typeof n === "number") {

6.

result += n;

7.

}

8.

return this;

9.

},

10.

subtract: function(n) {

11.

if (typeof n === "number") {

12.

result -= n;

13.

}

14.

return this;

15.

},

16.

multiply: function(n) {

17.

if (typeof n === "number") {

18.

result *= n;

19.

}

20.

return this;

21.

},

22.

divide: function(n) {

23.

if (typeof n === "number" && n !== 0) {

24.

result /= n;

25.

}

26.

return this;

27.

},

28.

getResult: function() { return result; }

29.

};

30.

31.

return calculator;

32.

})();

Ryzykujemy jednak w takim przypadku:

czysty tekst

1.

var dodaj = calculator.add;

2.

dodaj(2).add(2).add(2).getResult();


Wyszukiwarka

Podobne podstrony:
Laboratorium 1 Na czym polega chaining w JavaScript
NA CZYM POLEGA SYNDROM POABORCYJNY, 5 - SYNDROM POSTABORCYJNY
48 Na czym polega różnica między zmiennymi Lagrangea i zmiennymi Eulera
Wyjaśnij na czym polega tzw. kwestia homerycka, P-Ż
Na czym polegała hybrydalność polskiego oświecenia, oświecenie(3)
104 Na czym polega Umowa z Schengenid745
na czym polega chip tuning
Procedury?dania sprawoazdania finansowego (na czym polega procedura wiarygodności, zgodności…)x
Na czym polega rozchodzenie się?li dźwiękowej
Na czym polega metoda Vojty
Na czym polega metoda chromatografii bibułowej, kosmetologia, chemia kosmetyczna
Na czym polega i jaką funkcję pełni poetyzacja rzeczywistości w Panu Tadeuszu
Na czym polegała przedstawiona przez D Hume’a krytyka konieczno¶ci zwi±zków przyczynowych
Na czym polega indukcja?dukcyjna kolumny
Na czym polega specyfika Potopu jako powieści historycznej
Na czym polega czasowe i logiczne pierszeństwo zdań protokolarnych, Czy czasowe i logiczne pierwszeń

więcej podobnych podstron