Słuchanie zmiennych zmian w JavaScript

462

Czy możliwe jest zdarzenie w JS, które jest uruchamiane, gdy zmienia się wartość określonej zmiennej? JQuery jest akceptowane.

rashcroft22
źródło
@BenjaminGruenbaum prawdopodobnie chcesz powiedzieć MutableObserver (dla DOM). Obiekt jest tylko dla obiektów JS z tego, co pamiętam.
HellBaby,
2
@HellBaby to pytanie dotyczy zmiennych - nie DOM.
Benjamin Gruenbaum
9
@BenjaminGruenbaum według developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Object.observe jest przestarzały lub przestarzały. Zalecanym zamiennikiem (dla tej samej strony) jest obiekt proxy.
stannius
4
Pytanie tylko o to variable, ale wszystkie odpowiedzi tutaj odnoszą się property. Zastanawiam się jednak, czy możemy nasłuchiwać local variablezmian.
Thariq Nugrohotomo
1
czy jest na to jakaś odpowiedź? Mam na myśli, aby wybrać jeden
Braian Mellor

Odpowiedzi:

100

Tak, jest to teraz całkowicie możliwe!

Wiem, że to stary wątek, ale teraz ten efekt jest możliwy przy użyciu akcesoriów (pobierających i ustawiających): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters

Możesz zdefiniować taki obiekt, w którym aInternalreprezentuje pole a:

x = {
  aInternal: 10,
  aListener: function(val) {},
  set a(val) {
    this.aInternal = val;
    this.aListener(val);
  },
  get a() {
    return this.aInternal;
  },
  registerListener: function(listener) {
    this.aListener = listener;
  }
}

Następnie możesz zarejestrować słuchacza za pomocą:

x.registerListener(function(val) {
  alert("Someone changed the value of x.a to " + val);
});

Tak więc, gdy cokolwiek zmieni wartość x.a, funkcja nasłuchiwania zostanie uruchomiona. Uruchomienie następującego wiersza spowoduje wyświetlenie wyskakującego powiadomienia:

x.a = 42;

Zobacz przykład tutaj: https://jsfiddle.net/5o1wf1bn/1/

Możesz także użyć tablicy słuchaczy zamiast jednego gniazda słuchacza, ale chciałem dać ci najprostszy możliwy przykład.

Akira
źródło
6
To jest mnóstwo kodu na obiekt, czy istnieje sposób, aby uczynić to bardziej użytecznym?
Vael Victus,
Gdzie jest twój kod Czy zacząłeś od tego nowe pytanie?
Akira
Co powiesz na sposób wykrywania, kiedy nowa właściwość została dodana do obiektu lub jedna została usunięta?
Michael
To stara odpowiedź, ale chciałem dodać, że działa to dobrze dla wartości tablic, o ile ustawiasz wartość tablicy zamiast wypychać do niej.
TabsNotSpaces,
1
Musisz po prostu mieć tablicę słuchaczy zamiast jednego, a następnie zamiast po prostu wywoływać this.aListener(val), będziesz musiał przeglądać wszystkie funkcje nasłuchiwania i wywoływać każde z nich val. Zazwyczaj metoda jest wywoływana addListenerzamiast registerListener.
Akira
74

Większość odpowiedzi na to pytanie jest przestarzała, nieskuteczna lub wymaga włączenia dużych, rozdętych bibliotek:

Obecnie możesz teraz używać obiektu Proxy do monitorowania (i przechwytywania) zmian wprowadzonych w obiekcie. Jest przeznaczony do tego, co OP chce zrobić. Oto podstawowy przykład:

var targetObj = {};
var targetProxy = new Proxy(targetObj, {
  set: function (target, key, value) {
      console.log(`${key} set to ${value}`);
      target[key] = value;
      return true;
  }
});

targetProxy.hello_world = "test"; // console: 'hello_world set to test'

Jedynymi wadami Proxyobiektu są:

  1. ProxyObiekt nie jest dostępny w starszych przeglądarek (takich jak IE11) oraz polyfill nie może w pełni kopią Proxyfunkcjonalności.
  2. Obiekty proxy nie zawsze zachowują się zgodnie z oczekiwaniami ze obiektami specjalnymi (np. Date) - Proxynajlepiej połączyć obiekt ze zwykłymi obiektami lub tablicami.

Jeśli chcesz obserwować zmiany dokonane w zagnieżdżonym obiekcie , musisz użyć specjalistycznej biblioteki, takiej jak Observable Slim (którą opublikowałem), która działa w następujący sposób:

var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
    console.log(JSON.stringify(changes));
});

p.testing.blah = 42; // console:  [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
Elliot B.
źródło
1
Dodam jeszcze jedną wadę, w rzeczywistości nie oglądasz zmian na obiekcie docelowym, a jedynie na obiekcie proxy. W niektórych przypadkach chcesz po prostu wiedzieć, kiedy właściwość zmienia się na obiekcie docelowym (tj. target.hello_world = "test")
Cristiano
2
you don't actually watch changes on the target object but only on proxy object- to nie do końca dokładne. ProxyObiekt nie jest modyfikowany - nie mieć swój własny egzemplarz cel. you just want to know when a property change on the target object- możesz to zrobić za pomocą Proxy, jest to jeden z głównych przypadków użycia serwerów proxy.
Elliot B.
2
Nie, ponieważ modyfikujesz cel bezpośrednio. Jeśli chcesz obserwować modyfikację target, musisz to zrobić przez proxy. Nie proxy.hello_world = "test"oznacza to jednak, że modyfikujesz serwer proxy, serwer proxy pozostaje niezmieniony, targetzostaje zmodyfikowany (jeśli Twój program obsługi jest skonfigurowany do tego). Wygląda na to, że Twoim celem jest to, że nie możesz bezpośrednio obserwować target.hello_world = "test". To prawda. Zwykłe przypisania zmiennych nie emitują żadnego zdarzenia. Dlatego musimy korzystać z narzędzi takich jak te opisane w odpowiedziach na to pytanie.
Elliot B.
2
Dziękuję Elliot B. Właśnie It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.o to mi chodzi. W moim przypadku mam obiekt utworzony gdzieś indziej i zaktualizowany przez inny kod ... proxy w tym przypadku nie jest przydatne, ponieważ zmiany zostaną wprowadzone w celu.
Cristiano
1
@Cristiano Myślę, że Elliot próbuje powiedzieć, że możesz użyć proxy zamiast rzeczywistego obiektu, co oznacza, że ​​możesz przekazać proxy tak, jakby to był twój obiekt i sprawić, aby inne części aplikacji współdziałały z twoim proxy. Zmiany w proxy zostaną odzwierciedlone na rzeczywistym obiekcie.
Zoltán Matók
33

Nie.

Ale jeśli to naprawdę tak ważne, masz 2 opcje (pierwsza jest testowana, druga nie):

Najpierw użyj seterów i getterów, takich jak:

var myobj = {a : 1};

function create_gets_sets(obj) { // make this a framework/global function
    var proxy = {}
    for ( var i in obj ) {
        if (obj.hasOwnProperty(i)) {
            var k = i;
            proxy["set_"+i] = function (val) { this[k] = val; };
            proxy["get_"+i] = function ()    { return this[k]; };
        }
    }
    for (var i in proxy) {
        if (proxy.hasOwnProperty(i)) {
            obj[i] = proxy[i];
        }
    }
}

create_gets_sets(myobj);

możesz zrobić coś takiego:

function listen_to(obj, prop, handler) {
    var current_setter = obj["set_" + prop];
    var old_val = obj["get_" + prop]();
    obj["set_" + prop] = function(val) { current_setter.apply(obj, [old_val, val]); handler(val));
}

następnie ustaw detektor w następujący sposób:

listen_to(myobj, "a", function(oldval, newval) {
    alert("old : " + oldval + " new : " + newval);
}

Po drugie, właściwie zapomniałem, prześlę się, gdy się nad tym zastanowię :)

EDYCJA: Och, pamiętam :) Możesz postawić zegarek na wartość:

Biorąc pod uwagę myobj powyżej, z „a” na nim:

function watch(obj, prop, handler) { // make this a framework/global function
    var currval = obj[prop];
    function callback() {
        if (obj[prop] != currval) {
            var temp = currval;
            currval = obj[prop];
            handler(temp, currval);
        }
    }
    return callback;
}

var myhandler = function (oldval, newval) {
    //do something
};

var intervalH = setInterval(watch(myobj, "a", myhandler), 100);

myobj.set_a(2);
Luke Schafer
źródło
8
„oglądanie” w rzeczywistości nie wykrywa zmiany. Nie jest to oparte na zdarzeniach iz pewnością bardzo szybko spowolniłoby całą aplikację. Takie podejście IMHO nigdy nie powinno być częścią prawdziwego projektu.
Muhammad bin Yusrat
28

Korzystanie Prototype: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

// Console
function print(t) {
  var c = document.getElementById('console');
  c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
var myVar = 123;

Object.defineProperty(this, 'varWatch', {
  get: function () { return myVar; },
  set: function (v) {
    myVar = v;
    print('Value changed! New value: ' + v);
  }
});

print(varWatch);
varWatch = 456;
print(varWatch);
<pre id="console">
</pre>

Inny przykład

// Console
function print(t) {
  var c = document.getElementById('console');
  c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
var varw = (function (context) {
  return function (varName, varValue) {
    var value = varValue;
  
    Object.defineProperty(context, varName, {
      get: function () { return value; },
      set: function (v) {
        value = v;
        print('Value changed! New value: ' + value);
      }
    });
  };
})(window);

varw('varWatch'); // Declare
print(varWatch);
varWatch = 456;
print(varWatch);

print('---');

varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>

Eduardo Cuomo
źródło
Drugi przykład jest nieco mylący, varwwymaga 2 argumentów, ale część twojego przykładu pokazuje, że funkcja jest wywoływana tylko z parametrem value.
Hlawuleka MAS
22

Przykro mi, że przywołałem stary wątek, ale oto mała instrukcja dla tych, którzy (jak ja!) Nie widzą, jak działa przykład Eli Grey:

var test = new Object();
test.watch("elem", function(prop,oldval,newval){
    //Your code
    return newval;
});

Mam nadzieję, że to może komuś pomóc

javascript jest przyszłością
źródło
9
Zegarek nie jest obecnie obsługiwany w Chrome ani Safari, tylko Firefox
Paul McClean,
5
W przypadku sieci deweloperów Mozilli nie jest to zalecane. Object.prototype.watch () był przeznaczony wyłącznie do testowania i nie powinien być używany w kodzie produkcyjnym. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Dennis Bartlett
4
@PaulMcClean ta odpowiedź była odpowiedzią na odpowiedź Eli Greya, która zawierała polifill. gist.github.com/eligrey/384583
Hobbyist
2
zegarek został wycofany przez Mozillę. Ta odpowiedź może wprowadzać w błąd.
Arnaud,
7

Jako odpowiedź Luke'a Schafera ( uwaga : odnosi się to do jego oryginalnego postu, ale cały punkt tutaj pozostaje ważny po edycji ), sugerowałbym również parę metod Get / Set, aby uzyskać dostęp do twojej wartości.

Sugerowałbym jednak pewne modyfikacje (i dlatego publikuję ...).

Problem z tym kodem polega na tym, że pole aobiektu myobjjest bezpośrednio dostępne, więc można uzyskać do niego dostęp / zmienić jego wartość bez wyzwalania detektorów:

var myobj = { a : 5, get_a : function() { return this.a;}, set_a : function(val) { this.a = val; }}
/* add listeners ... */
myobj.a = 10; // no listeners called!

Kapsułkowanie

Tak więc, aby zagwarantować, że słuchacze są faktycznie wezwani, musielibyśmy zabronić tego bezpośredniego dostępu do pola a. Jak to zrobić? Użyj zamknięcia !

var myobj = (function() { // Anonymous function to create scope.

    var a = 5;            // 'a' is local to this function
                          // and cannot be directly accessed from outside
                          // this anonymous function's scope

    return {
        get_a : function() { return a; },   // These functions are closures:
        set_a : function(val) { a = val; }  // they keep reference to
                                            // something ('a') that was on scope
                                            // where they were defined
    };
})();

Teraz możesz użyć tej samej metody do tworzenia i dodawania słuchaczy, jak zaproponował Luke, ale możesz być pewny, że nie ma możliwości, aby czytać i pisać aniezauważone!

Programowe dodawanie pól enkapsulowanych

Wciąż podążając śladem Luke'a, proponuję teraz prosty sposób dodawania do obiektów zamkniętych pól i odpowiednich modułów pobierających / ustawiających za pomocą prostego wywołania funkcji.

Pamiętaj, że będzie to działać poprawnie tylko z typami wartości . W tym celu do pracy z typami referencyjnymi , jakiś głęboki kopii musiałyby być realizowane (patrz ten jeden , na przykład).

function addProperty(obj, name, initial) {
    var field = initial;
    obj["get_" + name] = function() { return field; }
    obj["set_" + name] = function(val) { field = val; }
}

Działa to tak samo jak poprzednio: tworzymy zmienną lokalną na funkcji, a następnie tworzymy zamknięcie.

Jak tego użyć? Prosty:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
Bruno Reis
źródło
2
+1 za enkapsulację. To była moja pierwsza myśl, ale chciałem w końcu móc dodać metodę create_gets_sets, a ponieważ jest ona bezkrytyczna, ukrywanie wartości nie jest fajne :) możemy pójść o krok dalej i napisać kilka rzeczy, aby ukryć wartości, ale Wydaje mi się, że kod, który opublikowałem, jest dość mylący dla większości ludzi ... może jeśli jest do niego wezwanie ...
Luke Schafer
6

Jeśli używasz jQuery {UI} (z którego wszyscy powinni korzystać :-)), możesz użyć .change () z ukrytym elementem <input />.

Chuck Han
źródło
7
Nie do końca rozumiem. Jak możesz dołączyć zmienną do ukrytego <input/>elementu?
Peter Lee
4
Myślę, że Chuck sugeruje, że możesz ustawić wartość danych wejściowych za pomocą jquery, a następnie i detektora zdarzeń .change (). Jeśli zaktualizujesz wartość wejściową za pomocą .val (), wówczas zadzwoni wywołanie zwrotne zdarzenia .change ().
jarederaj
2
<input type="hidden" value="" id="thisOne" />i jQuery $("#thisOne").change(function() { do stuff here });i $("#thisOne").val(myVariableWhichIsNew);, a następnie .change()ogień.
khaverim
2
To najlepsze rozwiązanie w moich książkach. Proste, łatwe. Zamiast zmieniać zmienną w kodzie, np. var1 = 'new value';Zamiast tego ustawisz wartość tego ukrytego wejścia, a następnie dodasz detektor, aby zmienić zmienną. $("#val1").on('change', function(){ val1 = this.val(); ... do the stuff that you wanted to do when val1 changed... }); $("#val1").val('new value');
Tom Walker
2
Dla każdego, kto ma ten sam problem co ja, jeśli zdarzenie zmiany nie wyzwala, spróbuj $ („# thisOne”). Val (myVariableWhichIsNew) .trigger („zmiana”). Mam nadzieję, że to pomoże
Alator
6

AngularJS(Wiem, że tak nie jest JQuery, ale to może pomóc. [Czysta JS jest dobra tylko w teorii]):

$scope.$watch('data', function(newValue) { ..

gdzie „data” to nazwa zmiennej w zakresie.

Jest link do dokumentu.

ses
źródło
Niestety zmusza cię do powiązania zmiennej z zasięgiem
ruX,
jest uruchamiany tylko po $scope.$apply()uruchomieniu
iamawebgeek,
3

Funkcjonalności, której szukasz, można uzyskać za pomocą metody „replaceProperty ()” - dostępnej tylko w nowoczesnych przeglądarkach:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Napisałem rozszerzenie jQuery, które ma podobną funkcjonalność, jeśli potrzebujesz większej obsługi różnych przeglądarek:

https://github.com/jarederaj/jQueue

Małe rozszerzenie jQuery, które obsługuje wywołania zwrotne w kolejce do istnienia zmiennej, obiektu lub klucza. Można przypisać dowolną liczbę wywołań zwrotnych do dowolnej liczby punktów danych, na które mogą mieć wpływ procesy działające w tle. jQueue nasłuchuje i czeka, aż określone przez Ciebie dane pojawią się, a następnie odpala poprawne wywołanie zwrotne za pomocą swoich argumentów.

jarederaj
źródło
2

Nie bezpośrednio: potrzebujesz pary gettera / settera z interfejsem „addListener / removeListener” jakiegoś rodzaju ... lub wtyczki NPAPI (ale to zupełnie inna historia).

Jldupont
źródło
1
//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
    console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};

var x1 = {
    eventCurrentStatus:undefined,
    get currentStatus(){
        return this.eventCurrentStatus;
    },
    set currentStatus(val){
        this.eventCurrentStatus=val;
      //your function();
    }
};

lub

/*  var x1 = {
eventCurrentStatus:undefined,
currentStatus : {
    get : function(){
        return Events.eventCurrentStatus
        },
    set : function(status){
        Events.eventCurrentStatus=status;

    },
}*/
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);

lub

/* global variable ku*/
    var jsVarEvents={};
    Object.defineProperty(window, "globalvar1", {//no i18n
        get: function() { return window.jsVarEvents.globalvarTemp},
        set: function(value) { window.window.jsVarEvents.globalvarTemp = value; }
    });
    console.log(globalvar1);
    globalvar1=1;
    console.log(globalvar1);
Awatara
źródło
1

Dość prostym i uproszczonym rozwiązaniem jest po prostu użycie wywołania funkcji, aby ustawić wartość zmiennej globalnej i nigdy nie ustawiać jej bezpośrednio. W ten sposób masz całkowitą kontrolę:

var globalVar;

function setGlobalVar(value) {
    globalVar = value;
    console.log("Value of globalVar set to: " + globalVar);
    //Whatever else
}

Nie ma sposobu, aby to wymusić, wymaga jedynie dyscypliny programistycznej ... chociaż możesz użyć grep(lub czegoś podobnego), aby sprawdzić, czy nigdzie twój kod nie ustawia bezpośrednio wartości globalVar.

Lub możesz zamknąć to w obiektach i metodach pobierania i ustawiania użytkownika ... tylko myśl.

markvgti
źródło
Dla zmiennej, która nie jest własnością obiektu, do której można uzyskać dostęp - podobnie jak w przypadku zmiennych zadeklarowanych varw modułach ES6 - jest to jedyne rozwiązanie.
AMN
1

Proszę, pamiętajcie, że początkowe pytanie dotyczyło ZMIENNYCH, a nie OBIEKTÓW;)

oprócz wszystkich powyższych odpowiedzi stworzyłem małą bibliotekę o nazwie forTheWatch.js , która używa tego samego sposobu do i wywoływania zmian w normalnych zmiennych globalnych w javascript.

Kompatybilny ze zmiennymi JQUERY, nie trzeba używać OBIEKTÓW, w razie potrzeby można przekazać bezpośrednio tablicę kilku zmiennych.

Jeśli to może być pomocne ...: https://bitbucket.org/esabora/forthewatch
Zasadniczo wystarczy wywołać funkcję:
watchIt("theVariableToWatch", "varChangedFunctionCallback");

I przepraszam z góry, jeśli nie dotyczy.

Soufiane Benrazzouk
źródło
1

Najłatwiejszy sposób, jaki znalazłem, zaczynając od tej odpowiedzi :

// variable holding your data
const state = {
  count: null,
  update() {
    console.log(`this gets called and your value is ${this.pageNumber}`);
  },
  get pageNumber() {
    return this.count;
  },
  set pageNumber(pageNumber) {
    this.count = pageNumber;
    // here you call the code you need
    this.update(this.count);
  }
};

I wtedy:

state.pageNumber = 0;
// watch the console

state.pageNumber = 15;
// watch the console
Giorgio Tempesta
źródło
1

W moim przypadku próbowałem dowiedzieć się, czy jakakolwiek biblioteka, którą włączam do mojego projektu, redefiniuje moją window.player. Na początku mojego kodu po prostu:

Object.defineProperty(window, 'player', {
  get: () => this._player,
  set: v => {
    console.log('window.player has been redefined!');
    this._player = v;
  }
});
José Antonio Postigo
źródło
0

To nie jest bezpośrednio możliwe.

Można to jednak zrobić za pomocą CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent

Poniższa metoda przyjmuje tablicę nazw zmiennych jako dane wejściowe i dodaje detektor zdarzeń dla każdej zmiennej i wyzwala zdarzenie w przypadku wszelkich zmian wartości zmiennych.

Metoda wykorzystuje odpytywanie w celu wykrycia zmiany wartości. Możesz zwiększyć wartość limitu czasu w milisekundach.

function watchVariable(varsToWatch) {
    let timeout = 1000;
    let localCopyForVars = {};
    let pollForChange = function () {
        for (let varToWatch of varsToWatch) {
            if (localCopyForVars[varToWatch] !== window[varToWatch]) {
                let event = new CustomEvent('onVar_' + varToWatch + 'Change', {
                    detail: {
                        name: varToWatch,
                        oldValue: localCopyForVars[varToWatch],
                        newValue: window[varToWatch]
                    }
                });
                document.dispatchEvent(event);
                localCopyForVars[varToWatch] = window[varToWatch];
            }
        }
        setTimeout(pollForChange, timeout);
    };
    let respondToNewValue = function (varData) {
        console.log("The value of the variable " + varData.name + " has been Changed from " + varData.oldValue + " to " + varData.newValue + "!!!"); 
    }
    for (let varToWatch of varsToWatch) {
        localCopyForVars[varToWatch] = window[varToWatch];
        document.addEventListener('onVar_' + varToWatch + 'Change', function (e) {
            respondToNewValue(e.detail);
        });
    }
    setTimeout(pollForChange, timeout);
}

Wywołując metodę:

watchVariables(['username', 'userid']);

Wykryje zmiany w zmiennych nazwa użytkownika i identyfikator użytkownika.

Ketan Yekale
źródło
0

Za pomocą gettera i settera możesz zdefiniować klasę JavaScript, która robi takie rzeczy.

Najpierw definiujemy naszą klasę o nazwie MonitoredVariable:

class MonitoredVariable {
  constructor(initialValue) {
    this._innerValue = initialValue;
    this.beforeSet = (newValue, oldValue) => {};
    this.beforeChange = (newValue, oldValue) => {};
    this.afterChange = (newValue, oldValue) => {};
    this.afterSet = (newValue, oldValue) => {};
  }

  set val(newValue) {
    const oldValue = this._innerValue;
    // newValue, oldValue may be the same
    this.beforeSet(newValue, oldValue);
    if (oldValue !== newValue) {
      this.beforeChange(newValue, oldValue);
      this._innerValue = newValue;
      this.afterChange(newValue, oldValue);
    }
    // newValue, oldValue may be the same
    this.afterSet(newValue, oldValue);
  }

  get val() {
    return this._innerValue;
  }
}

Załóżmy, że chcemy nasłuchiwać moneyzmian, utwórzmy instancję MonitoredVariablez początkowymi pieniędzmi 0:

const money = new MonitoredVariable(0);

Następnie możemy uzyskać lub ustawić jego wartość za pomocą money.val:

console.log(money.val); // Get its value
money.val = 2; // Set its value

Ponieważ nie zdefiniowaliśmy dla niego żadnych słuchaczy, po money.valzmianie na 2 nie dzieje się nic specjalnego .

Teraz zdefiniujmy niektórych słuchaczy. Mamy cztery słuchaczy dostępny: beforeSet, beforeChange, afterChange, afterSet. Następujące czynności będą się pojawiać sekwencyjnie, gdy money.val = newValuezmienisz wartość zmiennej:

  1. money.beforeSet (newValue, oldValue);
  2. money.beforeChange (newValue, oldValue); (Zostanie pominięty, jeśli jego wartość nie ulegnie zmianie)
  3. money.val = newValue;
  4. money.afterChange (newValue, oldValue); (Zostanie pominięty, jeśli jego wartość nie ulegnie zmianie)
  5. money.afterSet (newValue, oldValue);

Teraz definiujemy afterChangenasłuchiwanie, które będzie uruchamiane dopiero po money.valzmianie (podczas gdy afterSetzostanie uruchomione, nawet jeśli nowa wartość jest taka sama jak stara):

money.afterChange = (newValue, oldValue) => {
  console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};

Teraz ustaw nową wartość 3i zobacz, co się stanie:

money.val = 3;

W konsoli zobaczysz następujące informacje:

Money has been changed from 2 to 3

Pełny kod można znaleźć na stronie https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab .

yusanshi
źródło
-2

To jest stary wątek, ale natknąłem się na drugą najwyższą odpowiedź (niestandardowe odbiorniki), szukając rozwiązania za pomocą Angulara. Podczas gdy rozwiązanie działa, angular ma lepszą wbudowaną metodę rozwiązania tego problemu za pomocą @Outputemiterów zdarzeń i zdarzeń. Wyjście z przykładu w niestandardowej odpowiedzi odbiornika:

ChildComponent.html

<button (click)="increment(1)">Increment</button>

ChildComponent.ts

import {EventEmitter, Output } from '@angular/core';

@Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();

private myValue: number = 0;

public increment(n: number){
  this.myValue += n;

  // Send a change event to the emitter
  this.myEmitter.emit(this.myValue);
}

ParentComponent.html

<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>

ParentComponent.ts

public n: number = 0;

public monitorChanges(n: number){
  this.n = n;
  console.log(n);
}

To będzie teraz aktualizować nnadrzędnie za każdym razem, gdy kliknięty zostanie przycisk potomny. Działający stackblitz

TabsNotSpaces
źródło
-3
Utils = {
    eventRegister_globalVariable : function(variableName,handlers){
        eventRegister_JsonVariable(this,variableName,handlers);
    },
    eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
        if(jsonObj.eventRegisteredVariable === undefined) {
            jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
        }
        Object.defineProperty(jsonObj, variableName , {
                    get: function() { 
                        return jsonObj.eventRegisteredVariable[variableName] },
                    set: function(value) {
                        jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
                    });
            }
Awatara
źródło