Działanie "observable property"

0

Próbuje zgłębić temat wzorcu 'observable property' na pewnym przykładzie, ale w pewnym momencie nie rozumiem jak coś zostało wywołane.

Oto poniższy przykład:

var Book = function(name, price){
    var priceChanging = [],
        priceChanged = [];

    this.name = function(val){
        return name;
    };

    this.price = function(val){
        if (val !== undefined && val !== price){
            for(var i = 0; i < priceChanging.length; i++){
                if(!priceChanging[i](this, val)) {
                    return price;
                }
            }
            price = val;
            for(var i = 0; i < priceChanged.length; i++){
                priceChanged[i](this);
            }
        }
        return price;   
    };

    this.onPriceChanging = function(callback){
        priceChanging.push(callback);   
    };

    this.onPriceChanged = function(callback){
        priceChanged.push(callback);    
    };
};

var book = new Book('Javascript: The Good Parts', 23.99);

console.log('The name is: ' + book.name());

console.log('The price is: $' + book.price());

book.onPriceChanging(function (b, price) {
    if(price > 100){
        console.log('System error, price is too high!');
        return false;
    }
    return true;
});

book.onPriceChanged(function (b) {
    console.log('The book price has changed to: $' + b.price());
});

book.price(25);

book.price(100);
  1. Tworzę sobie konstruktor, definiuje właściwości i metody.
  2. Tworze sobie nową instancje.
  3. Czemu metody book.onPriceChanging i book.onPriceChanged zostały zdefiniowane poza konstruktorem ? Tzn. tam też zostały zdefiniowane, a teraz jakbym je redeklarował i pisał od nowa ?
  4. Wywołuje sobie book.price(25); . Czyli po raz pierwszy chcę zmienić cenę książki (właściwość price instancji book). Wracam sobie do metody price zdefiniowanej w konstruktorze. Sprawdzam warunki w if - wygląda na to, że spełnione. I teraz co ? Wygląda mi na to, że obie pętle for teraz nie ruszą, bo ich warunki nie są spełnione (nic nie ma w tablicach) i tylko przepisuje parametr value do właściwości price. Tu niespodzianka, bo zamiast spodziewanego '25', zostaje zwrócone 'The book price has changed to: $25'. gdzież w takim razie została ruszona 'book.onPriceChanged' ?

Jakby ktoś wytłumaczył powyższy przykład, ewentualnie dał jakieś wskazówki na co nie zwróciłem uwagi byłoby miło :) .

0
  1. One nie są definiowane. One są wywoływane. Popatrz co one robią - dodają podaną przez Ciebie funkcję do listy funkcji do wywołania w przypadku zmiany ceny (to co masz w metodzie price, co twierdzisz, że będzie puste). To jest coś identyczne do element.addEventListener("nazwa_zdarzenia", function() { /* obsluga */ }) czy tam jQuerowe $(element).bind('zdarzenie', function() {}) tylko nazwę zdarzenia masz w nazwie metody, którą wywołujesz (tak jak w jQuery $(element).click(funkcja))
0

Ok, więc te metody są wywoływane. Rzeczywiście podobne to jest np. do składni onClick. Ale wydaje mi się, że taki onClick, kiedy ma być uruchamiany (nie wiem, jakiś warunek, jakiś specyficzny sygnał) jest gdzieś zdeklarowane w core javascript, tymczasem ja mam tylko tyle w deklaracji:

this.onPriceChanging = function(callback){
        priceChanging.push(callback);   
    };

    this.onPriceChanged = function(callback){
        priceChanged.push(callback);    
    };

Chodzi mi o to, że nie bardzo widzę, gdzie jest zapisany warunek uruchamiania tych 'onPriceChanging', czy 'onPriceChanged'. Czy chodzi tylko o ten parametr price ? Do czego służy parametr b ? Jeśli tym szukanym przez mnie warunkiem uruchomienia jest parametr 'price' to sama zmiana ceny wywoływała by tylko pierwszą metodę 'onPriceChanging', a jak jest wywoływana druga ? Wywołanie pierwszej spowoduje uruchomienie pierwszej pętli w metodzie price, ale tu nie widzę już więcej powiązań.

1

Wywołanie obu masz przecież w w metodzie this.price.

Po kolei.

Spójrz na początek "klasy". Masz dwie tablice - priceChanging i priceChanged. W tych tablicach będą przechowywane callbacki - funkcje, do uruchomienia w specyficznym momencie.

Tablicę tę możesz modyfikować (dodawać elementy) wywołując obiekt.onPriceChanging(nowa_funkcja) lub obiekt.onPriceChanged(nowa_funkcja). Spójrz na zawartość tej metody. Do znanych nam z wyżej tablic priceChanging i priceChanged dodajesz nową warość (tablica.push(5) doda do tablicy wartość 5). Tymi wartościami są funkcje, które przekazujesz jako parametr (tu nazwane callback).

I teraz spójrz na metodę price. Przelatuje ona przez tablicę priceChanging - i wykonuje każdą z jej wartości tak jak każdą inną funkcję (priceChanging[i](this, val)). Jako parametry podaje this - bieżący obiekt - i podaną wartość. Te dwa parametry w callbacku, który dodajesz nazywają się b i price - ale możesz je tam sobie nazwać jak chcesz podając nowy callback.

I to w zasadzie tyle. Zwrócenie wartości, która przelicza się na false w którymkolwiek z callbacków onPriceChanging - jak widzisz - powoduje zwrócenie bieżącej ceny i nie zmienianie jest, czyli tak jak widać w przykładzie - możesz powstrzymać zmianę ceny, jeżeli z jakiegoś powodu Ci nie pasuje.

--

Kącik wiedzy ogólnej, bo zapewne nie rozumiesz idei przekazywania funkcji, też przez to przechodziłem:

// to jest funkcja, która przyjmuje inną funkcję i ją wykonuje
function wykonajMnie(callback) {
  console.log("wykonuję funkcję");
  callback();
  console.log("wykonano");
}

// to jest sposób na przekazanie funkcji jako parametr do innej funkcji
wykonajMnie(function() {
  console.log("przekazana funkcja");
});

// zróbmy to jednak czytelniejszym:

// to jest zwykła inna funkcja
function doPrzekazania() {
  console.log("przekazana funkcja");
}

// możesz ją też zdefiniować o tak:
var doPrzekazania2 = function() {
  console.log("przekazana funkcja 2");
};

// niezależnie od sposobu definiowania - możemy jedną funkcję przekazać jako parametr do drugiej:
wykonajMnie(doPrzekazania);

// ma to identyczne działanie z pokazanym powyżej (tu powtarzam)
wykonajMnie(function() {
  console.log("przekazana funkcja");
});

// oraz z tym:
wykonajMnie(doPrzekazania2);

// BARDZO WAŻNE
// zwróć uwagę, że przekazując funkcję - nie podajesz nawiasów - wtedy byś ją wykonał w momencie przekazywania i nie przekazałbyś funkcji, a jej wynik.
// Przyjrzyj się temu:

wykonajMnie(doPrzekazania); // przekazanie funkcji
wykonajMnie(doPrzekazania()); // przekazanie undefined, ponieważ funkcja doPrzekazania nic nie zwraca

// Podobny przykład:
var dajMiDwa = function() { return 2; };

var coDostalem = function(param) {
  return typeof param;
};

alert(coDostalem(dajMiDwa)); // wyświetli "function"
alert(coDostalem(dajMiDwa())); // wyświetli "number"

// I ostatnia rzecz - parametry
// U Ciebie to metoda price wykonuje przekazane wcześniej callbacki - stąd to tamta funkcja decyduje - co przekazać

var mojaFunkcja = function(callback) {
  var parametrZKosmosu = 1;
  var parametrInny = 3;
  return callback(parametrZKosmosu, parametrInny); // do przekazanej funkcji przekazujemy jakieśtam zmienne potrzebne w danym momencie
};

// zdefiniujemy sobie teraz funkcję, która coś robi z podanymi parametrami
var dodawanie = function(num1, num2) {
  return num1+num2;
};

var odejmowanie = function(num1, num2) {
  return num1-num2;
};

mojaFunkcja(dodawanie); // zwróci 4
mojaFunkcja(odejmowanie); // zwróci -2

// i może temat przechowywania funkcji w tablicy

var arr = [
  dodawanie,
  function(num1, num2) { return num1*num2; },
  "to nie jest funkcja",
  odejmowanie()
];

// Zapisaliśmy w tablicy 4 wartości:
// funkcję
// drugą funkcję, zdefiniowaną bezpośrednio w parametrze, niezapisaną do zmiennej
// stringa
// wynik funkcji odejmowanie - ponieważ podaliśmy nawiasy = wykonaliśmy ją i jej wartość zapisujemy do zmiennej - btw: próba odejmowania od siebie `undefined` zwróci nam `NaN`

// "przelećmy" sobie teraz naszą tablicę:

for (var i = 0; i < arr.length; i++) {
  if (typeof arr[i] === "function") {
    alert("Parametr "+i+" to była funkcja, więc wrzucamy jej dwie dwójki i dostajemy: " + arr[i](2, 2)
  }
  else {
    alert("Parametr "+i+" to nie była funkcja, a "+typeof(arr[i])+" o wartości "+arr[i]+" więc nie możemy tego wykonać podając temu dwójki");
  }
}

Wszystko pisane z palca, więc mogą być literówki.
Mam nadzieję, że to Ci rozjaśnia temat.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0