Auf variable Änderungen in JavaScript warten

462

Ist es möglich, dass in JS ein Ereignis ausgelöst wird, wenn sich der Wert einer bestimmten Variablen ändert? JQuery wird akzeptiert.

rashcroft22
quelle
@BenjaminGruenbaum wahrscheinlich möchten Sie MutableObserver (für DOM) sagen. Objekt ist nur für JS-Objekte, soweit ich mich erinnere.
HellBaby
2
@HellBaby Bei dieser Frage geht es um Variablen - nicht um das DOM.
Benjamin Gruenbaum
9
@BenjaminGruenbaum laut developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Object.observe ist veraltet oder veraltet. Der empfohlene Ersatz (auf derselben Seite) ist das Proxy-Objekt.
Stannius
4
Die Frage, nach der nur gefragt wird variable, aber alle Antworten hier beziehen sich auf property. Ich frage mich, ob wir auf local variableVeränderungen hören können .
Thariq Nugrohotomo
1
Gibt es eine Antwort darauf? Ich meine, um einen auszuwählen
Braian Mellor

Antworten:

100

Ja, das ist jetzt völlig möglich!

Ich weiß, dass dies ein alter Thread ist, aber jetzt ist dieser Effekt mit Accessoren (Getter und Setter) möglich: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters

Sie können ein Objekt wie dieses definieren, in dem aInternaldas Feld dargestellt wird 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;
  }
}

Anschließend können Sie einen Listener wie folgt registrieren:

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

Wenn also etwas den Wert von ändert x.a, wird die Listener-Funktion ausgelöst. Wenn Sie die folgende Zeile ausführen, wird das Alarm-Popup angezeigt:

x.a = 42;

Ein Beispiel finden Sie hier: https://jsfiddle.net/5o1wf1bn/1/

Sie können auch ein Array von Listenern anstelle eines einzelnen Listener-Slots verwenden, aber ich wollte Ihnen das einfachste Beispiel geben.

Akira
quelle
6
Dies ist eine Menge Code pro Objekt. Gibt es eine Möglichkeit, dies wiederverwendbarer zu machen?
Vael Victus
Wo ist dein Code? Hast du eine neue Frage daraus angefangen?
Akira
Was ist mit einer Möglichkeit zu erkennen, wann einem Objekt eine neue Eigenschaft hinzugefügt oder eine entfernt wurde?
Michael
Dies ist eine alte Antwort, aber ich wollte hinzufügen, dass dies für Array-Werte gut funktioniert, solange Sie den Wert des Arrays festlegen, anstatt darauf zu pushen.
TabsNotSpaces
1
Sie müssen lediglich ein Array von Listenern anstelle eines einzelnen haben, und dann müssen Sie nicht nur anrufen this.aListener(val), sondern alle Listener-Funktionen durchlaufen und jeden einzelnen Listener aufrufen val. In der Regel wird die Methode addListeneranstelle von aufgerufen registerListener.
Akira
74

Die meisten Antworten auf diese Frage sind entweder veraltet, ineffektiv oder erfordern die Aufnahme großer aufgeblähter Bibliotheken:

  • Object.watch und Object.observe sind beide veraltet und sollten nicht verwendet werden.
  • onPropertyChange ist ein DOM-Element-Ereignishandler, der nur in einigen Versionen von IE funktioniert.
  • Mit Object.defineProperty können Sie eine Objekteigenschaft unveränderlich machen, wodurch Sie versuchte Änderungen erkennen, aber auch Änderungen blockieren können.
  • Das Definieren von Setzern und Gettern funktioniert, erfordert jedoch viel Setup-Code und funktioniert nicht gut, wenn Sie neue Eigenschaften löschen oder erstellen müssen.

Heute können Sie das Proxy- Objekt verwenden , um an einem Objekt vorgenommene Änderungen zu überwachen (und abzufangen). Es wurde speziell für das entwickelt, was das OP zu tun versucht. Hier ist ein einfaches Beispiel:

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'

Die einzigen Nachteile des ProxyObjekts sind:

  1. Das ProxyObjekt ist in älteren Browsern (z. B. IE11) nicht verfügbar, und die Polyfüllung kann die ProxyFunktionalität nicht vollständig replizieren .
  2. Proxy-Objekte verhalten sich bei speziellen Objekten (z. B. Date) nicht immer wie erwartet - das ProxyObjekt wird am besten mit einfachen Objekten oder Arrays gepaart.

Wenn Sie Änderungen an einem verschachtelten Objekt beobachten müssen , müssen Sie eine spezielle Bibliothek wie Observable Slim (die ich veröffentlicht habe) verwenden, die folgendermaßen funktioniert:

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.
quelle
1
Ich werde noch einen weiteren Nachteil hinzufügen: Sie beobachten keine Änderungen am Zielobjekt, sondern nur am Proxy-Objekt. In einigen Fällen möchten Sie nur wissen, wann sich eine Eigenschaft am Zielobjekt (dh target.hello_world = "test") ändert
Cristiano
2
you don't actually watch changes on the target object but only on proxy object- Das ist nicht ganz richtig. Das ProxyObjekt wird nicht geändert - es verfügt nicht über eine eigene Kopie des Ziels. you just want to know when a property change on the target object- Sie können dies mit a erreichen Proxy, das ist einer der primären Anwendungsfälle für Proxys.
Elliot B.
2
Nein, weil Sie das Ziel direkt ändern. Wenn Sie die Änderung an beobachten möchten, targetmüssen Sie dies über einen Proxy tun. Dies proxy.hello_world = "test"bedeutet jedoch nicht, dass Sie den Proxy ändern. Der Proxy bleibt unverändert und targetwird geändert (sofern Ihr Set-Handler dafür konfiguriert ist). Es hört sich so an, als ob Sie nicht direkt beobachten können target.hello_world = "test". Das ist wahr. Einfache Variablenzuweisungen geben kein Ereignis aus. Deshalb müssen wir Werkzeuge verwenden, wie sie in den Antworten auf diese Frage beschrieben sind.
Elliot B.
2
Danke Elliot B. It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true.das ist genau mein Punkt. In meinem Fall habe ich ein Objekt an einer anderen Stelle erstellt und durch einen anderen Code aktualisiert. In diesem Fall ist ein Proxy nicht hilfreich, da die Änderungen am Ziel vorgenommen werden.
Cristiano
1
@Cristiano Ich denke, Elliot versucht zu sagen, dass Sie den Proxy anstelle des eigentlichen Objekts verwenden können, was bedeutet, dass Sie den Proxy so weitergeben können, als wäre es Ihr Objekt, und die anderen Teile der App mit Ihrem Proxy interagieren lassen. Änderungen am Proxy wirken sich auf das eigentliche Objekt aus.
Zoltán Matók
33

Nein.

Aber wenn es wirklich so wichtig ist, haben Sie zwei Möglichkeiten (die erste wird getestet, die zweite nicht):

Verwenden Sie zunächst Setter und Getter wie folgt:

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);

dann kannst du so etwas machen wie:

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));
}

Stellen Sie dann den Listener wie folgt ein:

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

Zweitens habe ich tatsächlich vergessen, ich werde einreichen, während ich darüber nachdenke :)

EDIT: Oh, ich erinnere mich :) Sie könnten eine Uhr auf den Wert setzen:

Angesichts von myobj oben mit 'a' darauf:

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
quelle
8
"Beobachten" erkennt tatsächlich keine Änderung. Dies ist nicht ereignisbasiert und würde sicherlich die gesamte App sehr, sehr bald verlangsamen. Diese Ansätze sollten meiner Meinung nach niemals Teil eines echten Projekts sein.
Muhammad bin Yusrat
28

Verwenden von 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>

Anderes Beispiel

// 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
quelle
Das zweite Beispiel ist etwas irreführend, varwerfordert 2 Argumente, aber ein Teil Ihres Beispiels zeigt die aufgerufene Funktion nur mit dem Wertparameter.
Hlawuleka MAS
22

Es tut mir leid, einen alten Thread aufzurufen, aber hier ist ein kleines Handbuch für diejenigen, die (wie ich!) Nicht sehen, wie das Beispiel von Eli Grey funktioniert:

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

Hoffe das kann jemandem helfen

Javascript ist Zukunft
quelle
9
Watch wird derzeit nicht auf Chrome oder Safari unterstützt, nur Firefox
Paul McClean
5
Pro Mozilla-Entwicklernetzwerk wird dies nicht empfohlen. Object.prototype.watch () war nur zum Testen gedacht und sollte nicht im Produktionscode verwendet werden. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Dennis Bartlett
4
@PaulMcClean Diese Antwort war eine Antwort auf Eli Greys Antwort, die eine Polyfüllung enthielt. gist.github.com/eligrey/384583
Hobbyist
2
Uhr wurde von Mozilla veraltet. Diese Antwort könnte irreführend sein.
Arnaud
7

Als Antwort von Luke Schafer ( Hinweis : Dies bezieht sich auf seinen ursprünglichen Beitrag; der ganze Punkt hier bleibt jedoch nach der Bearbeitung gültig ) würde ich auch ein Paar Get / Set-Methoden vorschlagen, um auf Ihren Wert zuzugreifen.

Ich würde jedoch einige Änderungen vorschlagen (und deshalb poste ich ...).

Ein Problem mit diesem Code besteht darin, dass auf das Feld ades Objekts myobjdirekt zugegriffen werden kann, sodass es möglich ist, darauf zuzugreifen / seinen Wert zu ändern, ohne die Listener auszulösen:

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!

Verkapselung

Um sicherzustellen, dass die Zuhörer tatsächlich angerufen werden, müssten wir diesen direkten Zugriff auf das Feld verbieten a. Wie geht das? Verwenden Sie einen Verschluss !

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
    };
})();

Jetzt können Sie die Listener auf dieselbe Weise erstellen und hinzufügen, wie von Luke vorgeschlagen, aber Sie können sicher sein, dass es keine Möglichkeit gibt a, unbemerkt zu lesen oder zu schreiben !

Programmiertes Hinzufügen gekapselter Felder

Immer noch auf Lukes Spur, schlage ich jetzt eine einfache Möglichkeit vor, gekapselte Felder und die entsprechenden Getter / Setter mithilfe eines einfachen Funktionsaufrufs zu Objekten hinzuzufügen.

Beachten Sie, dass dies nur dann richtig funktionieren mit Werttypen . Damit dies funktioniert mit Referenztypen , eine Art tiefer Kopie müßte (siehe implementiert diese ein , zum Beispiel).

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

Dies funktioniert genauso wie zuvor: Wir erstellen eine lokale Variable für eine Funktion und anschließend einen Abschluss.

Wie benutzt man es? Einfach:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
Bruno Reis
quelle
2
+1 für die Kapselung. Das war mein erster Gedanke, aber ich wollte die Möglichkeit haben, die Methode create_gets_sets irgendwann hinzuzufügen, und da sie wahllos ist, ist das Ausblenden der Werte nicht cool :) Wir können noch einen Schritt weiter gehen und einige Dinge schreiben, um die Werte auszublenden, aber Ich denke, der Code, den ich gepostet habe, ist für die meisten Leute verwirrend genug ... vielleicht, wenn es darum geht ...
Luke Schafer
6

Wenn Sie jQuery {UI} verwenden (das jeder verwenden sollte :-)), können Sie .change () mit einem versteckten <input /> -Element verwenden.

Chuck Han
quelle
7
Ich verstehe nicht ganz. Wie können Sie eine Variable an ein verstecktes <input/>Element anhängen ?
Peter Lee
4
Ich denke, Chuck schlägt vor, dass Sie den Wert der Eingabe mit jquery und then und einem .change () -Ereignis-Listener festlegen können. Wenn Sie den Wert der Eingabe mit .val () aktualisieren, wird der Ereignisrückruf .change () ausgelöst.
Jarederaj
2
<input type="hidden" value="" id="thisOne" />und mit jQuery $("#thisOne").change(function() { do stuff here });und $("#thisOne").val(myVariableWhichIsNew);dann dem .change()Willen Feuer.
Khaverim
2
Dies ist die beste Lösung in meinen Büchern. Einfach, leicht. Anstatt beispielsweise die Variable in Ihrem Code var1 = 'new value';zu ändern, legen Sie stattdessen den Wert dieser versteckten Eingabe fest und fügen dann einen Listener hinzu, um die Variable zu ändern. $("#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
Für alle, die das gleiche Problem wie ich haben, wenn das Änderungsereignis nicht ausgelöst wird, versuchen Sie $ ("# thisOne"). Val (myVariableWhichIsNew) .trigger ('change'). Hoffentlich hilft das
Alator
6

AngularJS(Ich weiß, dass dies nicht der Fall ist JQuery, aber das könnte helfen. [Pure JS ist nur theoretisch gut]):

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

Dabei ist "Daten" der Name Ihrer Variablen im Bereich.

Es gibt einen Link zu doc.

ses
quelle
Leider zwingt es Sie, Variable an Umfang zu binden
ruX
es wird nur abgefeuert, wenn $scope.$apply()es ausgeführt wird
iamawebgeek
5

Für diejenigen, die ein paar Jahre später abstimmen:

Für die meisten Browser (und IE6 +) ist eine Lösung verfügbar, die das Ereignis onpropertychange und die neuere Spezifikation defineProperty verwendet. Der kleine Haken ist, dass Sie Ihre Variable zu einem Dom-Objekt machen müssen.

Alle Einzelheiten:

http://johndyer.name/native-browser-get-set-properties-in-javascript/

MandoMando
quelle
3

Die Funktionalität, nach der Sie suchen, kann mithilfe der Methode "defineProperty ()" erreicht werden, die nur modernen Browsern zur Verfügung steht:

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

Ich habe eine jQuery-Erweiterung geschrieben, die ähnliche Funktionen bietet, wenn Sie mehr browserübergreifende Unterstützung benötigen:

https://github.com/jarederaj/jQueue

Eine kleine jQuery-Erweiterung, die Rückrufe in die Warteschlange für das Vorhandensein einer Variablen, eines Objekts oder eines Schlüssels verarbeitet. Sie können einer beliebigen Anzahl von Datenpunkten eine beliebige Anzahl von Rückrufen zuweisen, die möglicherweise von Prozessen betroffen sind, die im Hintergrund ausgeführt werden. jQueue hört zu und wartet, bis diese von Ihnen angegebenen Daten vorhanden sind, und löst dann den richtigen Rückruf mit seinen Argumenten aus.

jarederaj
quelle
2

Nicht direkt: Sie benötigen ein Paar Getter / Setter mit einer "addListener / removeListener" -Schnittstelle ... oder einem NPAPI-Plugin (aber das ist insgesamt eine andere Geschichte).

jldupont
quelle
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();
    }
};

oder

/*  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);

oder

/* 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);
Benutzerbild
quelle
1

Eine ziemlich einfache und vereinfachende Lösung besteht darin, nur einen Funktionsaufruf zu verwenden, um den Wert der globalen Variablen festzulegen, und niemals ihren Wert direkt festzulegen. Auf diese Weise haben Sie die vollständige Kontrolle:

var globalVar;

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

Es gibt keine Möglichkeit, dies durchzusetzen, es erfordert lediglich Programmierdisziplin ... obwohl Sie grep(oder etwas Ähnliches) verwenden können, um zu überprüfen, ob Ihr Code den Wert von nirgendwo direkt festlegtglobalVar .

Oder Sie können es in ein Objekt und Benutzer-Getter- und Setter-Methoden einkapseln ... nur ein Gedanke.

markvgti
quelle
Für eine Variable, die keine Eigenschaft eines Objekts ist, auf die varzugegriffen werden kann - wie dies bei Variablen der Fall ist, mit denen in ES6-Modulen deklariert wurde - ist dies die einzige Lösung.
amn
1

Bitte Jungs, erinnert euch, die erste Frage war für VARIABLEN, nicht für OBJEKTE;)

Zusätzlich zu allen obigen Antworten habe ich eine winzige Bibliothek namens forTheWatch.js erstellt , die auf dieselbe Weise Änderungen an normalen globalen Variablen in Javascript abfängt und zurückruft .

Kompatibel mit JQUERY-Variablen, keine Verwendung von OBJECTS erforderlich, und Sie können bei Bedarf direkt ein ARRAY mehrerer Variablen übergeben.

Wenn es hilfreich sein kann ...: https://bitbucket.org/esabora/forthewatch
Grundsätzlich müssen Sie nur die Funktion aufrufen:
watchIt("theVariableToWatch", "varChangedFunctionCallback");

Und Entschuldigung im Voraus, wenn nicht relevant.

Soufiane Benrazzouk
quelle
1

Der einfachste Weg, den ich gefunden habe, ausgehend von dieser Antwort :

// 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);
  }
};

Und dann:

state.pageNumber = 0;
// watch the console

state.pageNumber = 15;
// watch the console
Giorgio Tempesta
quelle
1

In meinem Fall habe ich versucht herauszufinden, ob eine Bibliothek, die ich in mein Projekt aufgenommen habe, meine neu definiert hat window.player. Zu Beginn meines Codes habe ich einfach Folgendes getan:

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

Es ist nicht direkt möglich.

Dies kann jedoch mit CustomEvent erfolgen: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent

Die folgende Methode akzeptiert ein Array von Variablennamen als Eingabe und fügt einen Ereignis-Listener für jede Variable hinzu und löst das Ereignis für Änderungen am Wert der Variablen aus.

Die Methode verwendet Polling, um die Änderung des Werts zu erkennen. Sie können den Wert für das Zeitlimit in Millisekunden erhöhen.

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);
}

Durch Aufrufen der Methode:

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

Es erkennt die Änderungen an den Variablen Benutzername und Benutzer-ID.

Ketan Yekale
quelle
0

Mit Hilfe von Getter und Setter können Sie eine JavaScript-Klasse definieren, die so etwas tut.

Zuerst definieren wir unsere Klasse mit dem Namen 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;
  }
}

Angenommen, wir möchten auf moneyÄnderungen warten, erstellen wir eine Instanz MonitoredVariablemit anfänglichem Geld 0:

const money = new MonitoredVariable(0);

Dann könnten wir seinen Wert erhalten oder einstellen, indem wir money.val:

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

Da wir keine Listener dafür definiert haben, passiert nach money.valÄnderungen an 2 nichts Besonderes .

Definieren wir nun einige Listener. Wir haben vier Hörer zur Verfügung: beforeSet, beforeChange, afterChange, afterSet. Folgendes geschieht nacheinander, wenn Sie money.val = newValueden Wert der Variablen ändern:

  1. money.beforeSet (newValue, oldValue);
  2. money.beforeChange (newValue, oldValue); (Wird übersprungen, wenn der Wert nicht geändert wird.)
  3. money.val = newValue;
  4. money.afterChange (newValue, oldValue); (Wird übersprungen, wenn der Wert nicht geändert wird.)
  5. money.afterSet (newValue, oldValue);

Jetzt definieren wir den afterChangeListener, der erst ausgelöst wird, nachdem er money.valsich geändert hat (während afterSeter ausgelöst wird, auch wenn der neue Wert mit dem alten identisch ist):

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

Stellen Sie nun einen neuen Wert ein 3und sehen Sie, was passiert:

money.val = 3;

In der Konsole wird Folgendes angezeigt:

Money has been changed from 2 to 3

Den vollständigen Code finden Sie unter https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab .

Yusanshi
quelle
-2

Dies ist ein alter Thread, aber ich bin auf die zweithöchste Antwort gestoßen (benutzerdefinierte Listener), als ich nach einer Lösung mit Angular gesucht habe. Während die Lösung funktioniert, verfügt Angular über eine bessere integrierte Methode, um dieses Problem mithilfe von @Outputund Ereignisemittenten zu beheben . Verlassen Sie das Beispiel in der benutzerdefinierten Listener-Antwort:

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);
}

Dies wird nun nbei jedem Klicken auf die untergeordnete Schaltfläche auf dem übergeordneten Element aktualisiert . Arbeits stackblitz

TabsNotSpaces
quelle
-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]);}
                    });
            }
Benutzerbild
quelle