Ascultarea modificărilor de variabile în JavaScript (Programare, Javascript, Jquery, Evenimente Dom)

rashcroft22 a intrebat.

Este posibil să aveți un eveniment în JS care se declanșează atunci când se schimbă valoarea unei anumite variabile? JQuery este acceptat.

Comentarii

  • @BenjaminGruenbaum probabil că vrei să spui MutableObserver (pentru DOM). Object este doar pentru obiecte JS din câte îmi amintesc. –  > Por HellBaby.
  • @HellBaby această întrebare se referă la variabile – nu la DOM. –  > Por Benjamin Gruenbaum.
  • @BenjaminGruenbaum conform developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Object.observe este învechit sau depreciat. Înlocuitorul recomandat (conform aceleiași pagini) este obiectul Proxy. –  > Por stannius.
  • Întrebarea se referă doar la variable, însă toate răspunsurile de aici se referă la property. Mă întreb dacă putem asculta pentru local variable schimbările totuși. –  > Por Thariq Nugrohotomo.
24 răspunsuri
Akira

Da, acest lucru este acum complet posibil!

Știu că acesta este un thread vechi, dar acum acest efect este posibil folosind accesori (getters și setters): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters

Puteți defini un obiect ca acesta, în care aInternal reprezintă câmpul 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;
  }
}

Apoi puteți înregistra un ascultător folosind următoarele: :

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

Astfel, ori de câte ori ceva modifică valoarea lui x.a, funcția de ascultare va fi declanșată. Rularea următoarei linii va aduce fereastra de alertă:

x.a = 42;

Vedeți un exemplu aici: https://jsfiddle.net/5o1wf1bn/1/

De asemenea, puteți utiliza o matrice de ascultători în loc de un singur slot de ascultător, dar am vrut să vă dau cel mai simplu exemplu posibil.

Comentarii

  • Ce ziceți de o modalitate de a detecta când o nouă proprietate a fost adăugată unui obiect sau când una a fost eliminată? –  > Por Michael.
  • Acesta este un răspuns mai vechi, dar am vrut să adaug că acest lucru funcționează bine și pentru valori de tablouri, atâta timp cât setați valoarea tabloului în loc să faceți „push” la el. –  > Por TabsNotSpaces.
  • @Akira Frumoasă soluție! Cum ar merge despre înregistrarea mai multor ascultători la aceeași variabilă? –  > Por Mike.
  • Trebuie pur și simplu să aveți o matrice de ascultători în loc de unul singur și apoi, în loc să apelați doar la this.aListener(val)ar trebui să parcurgeți în buclă toate funcțiile de ascultare și să le apelați pe fiecare dintre ele, trecând val. În mod obișnuit, metoda se numește addListener în loc de registerListener. –  > Por Akira.
Elliot B.

Această întrebare a fost postată inițial în 2009 și majoritatea răspunsurilor existente sunt fie depășite, fie ineficiente, fie necesită includerea unor biblioteci mari și umflate:

  • Object.watch și Object.observe sunt amândouă depreciate și nu ar trebui utilizate.
  • onPropertyChange este un gestionar de evenimente pentru elemente DOM care funcționează numai în anumite versiuni ale IE.
  • Object.defineProperty vă permite să faceți ca o proprietate a unui obiect să fie imuabilă, ceea ce v-ar permite să detectați încercările de modificare, dar ar bloca, de asemenea, orice modificare.
  • Definirea setterilor și getterilor funcționează, dar necesită o mulțime de cod de configurare și nu funcționează bine atunci când trebuie să ștergeți sau să creați proprietăți noi.

Începând cu 2018, puteți utiliza acum Proxy pentru a monitoriza (și intercepta) modificările aduse unui obiect. Acesta este construit special pentru ceea ce încearcă să facă OP. Iată un exemplu de bază:

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'

Singurele dezavantaje ale Proxy sunt:

  1. Proxy nu este disponibil în browserele mai vechi (cum ar fi IE11) și polyfill nu poate reproduce în totalitate Proxy funcționalitatea.
  2. Obiectele proxy nu se comportă întotdeauna conform așteptărilor cu obiecte speciale (de ex, Date) — se poate aplica Proxy este cel mai bine asociat cu obiecte simple sau array-uri.

Dacă aveți nevoie să observați modificările aduse unui obiect obiect imbricate, atunci trebuie să utilizați o bibliotecă specializată, cum ar fi Observable Slim (pe care am publicat-o). Aceasta funcționează astfel:

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}}]

 

Comentarii

  • Voi adăuga și un alt dezavantaj, nu urmărești de fapt schimbările de pe obiectul țintă, ci doar de pe obiectul proxy. În unele cazuri, doriți doar să știți când se schimbă o proprietate pe obiectul țintă (de exemplu, dacă se schimbă o proprietate pe obiectul proxy). target.hello_world = "test") –  > Por Cristiano.
  • you don't actually watch changes on the target object but only on proxy object — asta nu este chiar corect. Adresa Proxy nu este modificat – nu are o copie proprie a obiectului țintă. you just want to know when a property change on the target object — puteți realiza acest lucru cu un Proxy, acesta este unul dintre principalele cazuri de utilizare a proxy-urilor. –  > Por Elliot B..
  • Nu, pentru că modificați direct ținta. Dacă doriți să observați modificarea la targetatunci trebuie să o faci prin intermediul unui proxy. Cu toate acestea, proxy.hello_world = "test" nu înseamnă că modificați proxy-ul, proxy-ul rămâne neschimbat, target este modificat (dacă set handler-ul dvs. este configurat să facă acest lucru). Se pare că ceea ce vreți să spuneți este că nu puteți observa direct target.hello_world = "test". Acest lucru este adevărat. Atribuirile de variabile simple nu emit niciun fel de eveniment. De aceea, trebuie să folosim instrumente precum cele descrise în răspunsurile la această întrebare. –  > Por Elliot B..
  • Mulțumesc, Elliot B. It sounds like your point is that you cannot directly observe target.hello_world = "test". That is true. exact asta este și ideea mea. În cazul meu, am un obiect creat în altă parte și actualizat de un alt cod… un proxy, în acest caz, nu este util, deoarece modificările vor fi făcute pe țintă. –  > Por Cristiano.
  • @Cristiano Cred că ceea ce Elliot încearcă să spună este că puteți utiliza proxy-ul în loc de obiectului propriu-zis, ceea ce înseamnă că poți să transmiți proxy-ul ca și cum ar fi obiectul tău și să faci celelalte părți ale aplicației să interacționeze cu proxy-ul tău. Modificările de pe proxy se vor reflecta asupra obiectului real. –  > Por Zoltán Matók.
Luke Schafer

Nu.

Dar, dacă este chiar atât de important, aveți 2 opțiuni (prima este testată, a doua nu):

Prima, folosiți setters și getters, astfel:

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

apoi puteți face ceva de genul:

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

apoi să setați ascultătorul ca:

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

În al doilea rând, puteți pune un ceas pe valoare:

Având în vedere myobj de mai sus, cu „a” pe el:

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

Comentarii

  • „supravegherea” nu înseamnă de fapt detectarea unei modificări. Acest lucru nu se bazează pe evenimente și, cu siguranță, ar încetini foarte, foarte curând întreaga aplicație. Aceste abordări IMHO nu ar trebui să facă parte niciodată dintr-un proiect real. –  > Por Muhammad bin Yusrat.
Eduardo Cuomo

Comentarii

  • Al doilea exemplu este puțin înșelător, varw necesită 2 argumente, dar o parte din exemplul dvs. arată că funcția este apelată doar cu parametrul valoare. –  > Por Hlawuleka MAS.
javascript este viitorul

Îmi pare rău că aduc în discuție un subiect vechi, dar iată un mic manual pentru cei care (ca mine!) nu văd cum funcționează exemplul lui Eli Grey:

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

Sper că poate ajuta pe cineva

Comentarii

  • Deocamdată Watch nu este suportat de Chrome sau Safari, ci doar de Firefox –  > Por Paul McClean.
  • Conform rețelei de dezvoltatori Mozilla, acest lucru nu este recomandat. Object.prototype.watch() a fost conceput doar pentru testare și nu ar trebui să fie utilizat în codul de producție. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… –  > Por Dennis Bartlett.
  • @PaulMcClean acest răspuns a fost un răspuns la răspunsul lui Eli Grey, care conținea un Polyfill. gist.github.com/eligrey/384583 –  > Por Hobbyist.
  • watch a fost depreciat de Mozilla. Acest răspuns ar putea fi înșelător. –  > Por Arnaud.
Bruno Reis

După cum arată răspunsul lui Luke Schafer (nota: acesta se referă la postarea sa originală; dar întregul punct de vedere de aici rămâne valabil și după editare)), aș sugera, de asemenea, o pereche de metode Get/Set pentru a accesa valoarea dumneavoastră.

Cu toate acestea, aș sugera câteva modificări (și de aceea postez…).

O problemă cu acest cod este că câmpul a al obiectului myobj este direct accesibil, deci este posibil să îl accesați / să îi modificați valoarea fără a declanșa ascultătorii:

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!

Encapsulare

Așadar, pentru a garanta că ascultătorii sunt într-adevăr apelați, ar trebui să interzicem acel acces direct la câmp a. Cum se poate face acest lucru? Utilizați un închidere!

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

Acum puteți folosi aceeași metodă pentru a crea și a adăuga ascultătorii, așa cum a propus Luca, dar puteți fi siguri că nu există nicio modalitate posibilă de a citi sau de a scrie pe a să treacă neobservate!

Adăugarea câmpurilor încapsulate în mod programatic

Tot pe urmele lui Luke, propun acum o modalitate simplă de a adăuga câmpuri încapsulate și getteri/setteri respectivi la obiecte prin intermediul unui simplu apel de funcție.

Rețineți că acest lucru va funcționa în mod corespunzător numai cu tipuri de valori. Pentru ca acest lucru să funcționeze cu tipuri de referință, un fel de copie profundă ar trebui să fie implementat (a se vedea aceasta, de exemplu).

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

Acest lucru funcționează la fel ca înainte: creăm o variabilă locală pe o funcție și apoi creăm o închidere.

Cum să o folosim? Simplu:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);

Comentarii

  • +1 pentru încapsulare. Acesta a fost primul meu gând, dar am vrut să am posibilitatea de a adăuga metoda create_gets_sets în cele din urmă, și din moment ce este fără discriminare, ascunderea valorilor nu este cool 🙂 putem face un pas mai departe și să scriem niște lucruri pentru a ascunde valorile, dar cred că codul pe care l-am postat este destul de confuz pentru majoritatea oamenilor… poate dacă există cerere pentru asta… –  > Por Luke Schafer.
Chuck Han

Dacă folosiți jQuery {UI} (pe care toată lumea ar trebui să îl folosească 🙂 ), puteți folosi .change() cu un element <input/> ascuns.

Comentarii

  • Nu prea înțeleg. Cum poți atașa o variabilă la un element ascuns <input/> ascuns? –  > Por Peter Lee.
  • Cred că Chuck sugerează că puteți seta valoarea intrării folosind jquery și apoi un ascultător de evenimente .change(). Dacă actualizați valoarea intrării cu .val(), atunci se va declanșa apelul de eveniment .change(). –  > Por jarederaj.
  • <input type="hidden" value="" id="thisOne" /> și cu jQuery $("#thisOne").change(function() { do stuff here }); și $("#thisOne").val(myVariableWhichIsNew); și apoi .change() se va declanșa. –  > Por khaverim.
  • Aceasta este cea mai bună soluție din punctul meu de vedere. Simplu, ușor. În loc să schimbați variabila în cod, de ex. var1 = 'new value';, veți seta în schimb valoarea acestei intrări ascunse, apoi veți adăuga un ascultător pentru a schimba variabila. $("#val1").on('change', function(){ val1 = this.val(); ... do the stuff that you wanted to do when val1 changed... }); $("#val1").val('new value'); –  > Por Tom Walker.
  • Pentru oricine are aceeași problemă ca și mine, dacă evenimentul de schimbare nu se declanșează, încercați $(„#thisOne”).val(myVariableWhichIsNew).trigger(‘change’) .Sper că acest lucru vă ajută – –  > Por Alator.
ses

AngularJS (Știu că acest lucru nu este JQuery, dar asta ar putea ajuta. [Pure JS este bun doar în teorie]):

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

unde „data” este numele variabilei dvs. în domeniul de aplicare.

Există un link către doc.

Comentarii

  • Din păcate, vă obligă să legați variabila de domeniul de aplicare –  > Por ruX.
  • se declanșează numai atunci când $scope.$apply() este executat –  > Por iamawebgeek.
MandoMando

Pentru cei care se acordă cu câțiva ani mai târziu:

Este disponibilă o soluție pentru majoritatea browserelor (și IE6+) care utilizează evenimentul onpropertychange și specificația mai nouă defineProperty. Ușoara piedică este că va trebui să faceți din variabila dvs. un obiect dom.

Detalii complete:

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

Giorgio Tempesta

Cea mai ușoară cale pe care am găsit-o, pornind de la acest răspuns:

// 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 apoi:

state.pageNumber = 0;
// watch the console

state.pageNumber = 15;
// watch the console

jldupont

Nu direct: aveți nevoie de un getter/setter de perechi cu o interfață „addListener/removeListener” de un anumit fel… sau un plugin NPAPI (dar asta este o altă poveste cu totul alta).

jarederaj

Funcționalitatea pe care o căutați poate fi obținută prin utilizarea metodei „defineProperty()” – care este disponibilă doar pentru browserele moderne:

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

Am scris o extensie jQuery care are o funcționalitate similară, dacă aveți nevoie de mai mult suport pentru mai multe browsere:

https://github.com/jarederaj/jQueue

O mică extensie jQuery care se ocupă de punerea în coadă a callback-urilor la existența unei variabile, a unui obiect sau a unei chei. Puteți atribui orice număr de callback-uri la orice număr de puncte de date care ar putea fi afectate de procesele care rulează în fundal. jQueueue ascultă și așteaptă ca aceste date pe care le specificați să intre în existență și apoi declanșează callback-ul corect cu argumentele sale.

Rohan

Recent m-am trezit cu aceeași problemă. Am vrut să ascult la schimbarea unei variabile și să fac unele lucruri atunci când variabila s-a schimbat.

Cineva a sugerat o soluție simplă de a seta valoarea folosind un setter.

Declararea unui obiect simplu care păstrează valoarea variabilei mele aici:

var variableObject = {
    value: false,
    set: function (value) {
        this.value = value;
        this.getOnChange();
    }
}

Obiectul conține o metodă set prin care pot modifica valoarea. Dar, de asemenea, apelează o metodă getOnChange() metodă acolo. O voi defini acum.

variableObject.getOnChange = function() {
    if(this.value) {
        // do some stuff
    }
}

Acum, ori de câte ori voi face variableObject.set(true), metoda getOnChange metoda se declanșează, iar dacă valoarea a fost setată așa cum se dorește (în cazul meu: true), se execută și blocul if.

Acesta este cel mai simplu mod pe care l-am găsit pentru a face aceste lucruri.

José Antonio Postigo

În cazul meu, încercam să aflu dacă vreo bibliotecă pe care o includeam în proiectul meu redefinea window.player. Așa că, la începutul codului meu, am făcut:

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

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

sau

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

sau

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

markvgti

O soluție destul de simplă și simplistă este de a utiliza doar un apel de funcție pentru a seta valoarea variabilei globale și de a nu seta niciodată valoarea acesteia în mod direct. În acest fel, aveți un control total:

var globalVar;

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

Nu există nicio modalitate de a impune acest lucru, este nevoie doar de disciplină de programare… deși puteți folosi grep (sau ceva similar) pentru a verifica dacă codul dvs. nu stabilește nicăieri direct valoarea variabilei globalVar.

Sau ați putea să o încapsulați într-un obiect și să folosiți metode getter și setter… este doar o idee.

Comentarii

  • Pentru o variabilă care nu este o proprietate a unui obiect care poate fi accesată — cum este cazul variabilelor declarate cu var în modulele ES6 — aceasta este metoda singura soluție. –  > Por amn.
Soufiane Benrazzouk

Vă rog, băieți, amintiți-vă că întrebarea inițială era pentru VARIABILE, nu pentru OBIECTE 😉

în plus față de toate răspunsurile de mai sus, am creat o mică librărie numită forTheWatch.js, care utilizează același mod de a prinde și de a face callback pentru modificările variabilelor globale normale în javascript.

Compatibilă cu variabilele JQUERY, nu este nevoie să folosiți OBIECTE și puteți trece direct un ARRAY de mai multe variabile, dacă este necesar.

Dacă vă poate fi de ajutor… :https://bitbucket.org/esabora/forthewatch
Practic, trebuie doar să apelați funcția :
watchIt("theVariableToWatch", "varChangedFunctionCallback");

Și îmi cer scuze anticipat dacă nu este relevant.

yusanshi

Cu ajutorul lui getter și setter, puteți defini o clasă JavaScript care să facă un astfel de lucru.

În primul rând, definim clasa noastră numită 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;
  }
}

Să presupunem că dorim să ascultăm money modificări, să creăm o instanță a clasei MonitoredVariable cu bani inițiali 0:

const money = new MonitoredVariable(0);

Apoi putem obține sau seta valoarea sa folosind money.val:

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

Deoarece nu am definit niciun ascultător pentru el, nu se întâmplă nimic special după money.val se schimbă la 2.

Acum să definim niște ascultători. Avem patru ascultători disponibili: beforeSet, beforeChange, afterChange, afterSet.următoarele se vor întâmpla în mod secvențial atunci când utilizați money.val = newValue pentru a modifica valoarea variabilei:

  1. money.beforeSet(newValue, oldValue);
  2. money.beforeChange(newValue, oldValue); (Va fi sărit dacă valoarea sa nu se modifică)
  3. money.val = newValue;
  4. money.afterChange(newValue, oldValue); (Va fi ignorat dacă valoarea sa nu se modifică)
  5. money.afterSet(newValue, oldValue);

Acum definim afterChange ascultător care va fi declanșat numai după ce money.val s-a schimbat (while afterSet va fi declanșat chiar dacă noua valoare este aceeași cu cea veche):

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

Acum setează o nouă valoare 3 și vedeți ce se întâmplă:

money.val = 3;

Veți vedea următoarele în consolă:

Money has been changed from 2 to 3

Pentru codul complet, consultați https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab.

Comentarii

  • awsome … Vreau să extind acest exemplu de cod doar să ajung să știu ce licență este fragmentul de cod. +1 pentru a face reutilizabil prin clasă și înainte și după handlers –  > Por Syed.
Ketan Yekale

Nu este posibil în mod direct.

Cu toate acestea, acest lucru se poate face folosind CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent

Metoda de mai jos acceptă ca intrare o matrice de nume de variabile și adaugă un ascultător de evenimente pentru fiecare variabilă și declanșează evenimentul pentru orice modificare a valorii variabilelor.

Metoda utilizează polling pentru a detecta modificarea valorii. Puteți mări valoarea pentru timeout în milisecunde.

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

Prin apelarea metodei:

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

Aceasta va detecta modificările variabilelor username și userid.

Arsalan Ahmad Ishaq

Am venit aici căutând același răspuns pentru node js. Deci, aici este

const events = require('events');
const eventEmitter = new events.EventEmitter();

// Createing state to watch and trigger on change
let x = 10 // x is being watched for changes in do while loops below

do {
    eventEmitter.emit('back to normal');
}
while (x !== 10);

do {
    eventEmitter.emit('something changed');
}
while (x === 10);

Ceea ce fac este să setez niște emițătoare de evenimente atunci când se schimbă valorile și să folosesc buclele do while pentru a le detecta.

qiucw

Asta este ceea ce am făcut: Chemați JSON.stringify de două ori și comparați cele două șiruri…

Dezavantaje:

  • Puteți ști doar dacă se schimbă întregul obiect
  • Trebuie să detectați modificările manual
  • Ar fi bine să aveți numai câmpuri primitive în obiect (fără proprietăți, fără funcții…)

yurin

Am căutat o bibliotecă JavaScript de legare a datelor în două direcții și am dat peste aceasta.

Nu am reușit să o fac să funcționeze în DOM to variable direcție, dar în variable to DOM direcție funcționează și de asta avem nevoie aici.

L-am rescris ușor, deoarece codul original este foarte greu de citit (pentru mine). Acesta utilizeazăObject.defineProperty, deci al doilea cel mai votat răspuns de Eliot B. cel puțin parțial greșit.

<!DOCTYPE html>
<html>
  <head>
    <title>TODO supply a title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        const dataBind = (function () {
            const getElementValue = function (selector) {
                let element = document.querySelectorAll(selector)[0];
                return 'value' in element ? element.value : element.innerHTML;
            };
            const setElementValue = function (selector, newValue) {
                let elementArray = document.querySelectorAll(selector);
                for (let i = 0; i < elementArray.length; i++) {
                    let element = elementArray[i];
                    if ('value' in element) {
                        element.value = newValue;
                        if (element.tagName.toLowerCase() === 'select'){
                            let options = element.querySelectorAll('option');
                            for (let option in options){
                                if (option.value === newValue){
                                    option.selected = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        element.innerHTML = newValue;
                    }
                }
            };

            const bindModelToView = function (selector, object, property, enumerable) {
                Object.defineProperty(object, property, {
                    get: function () {
                        return getElementValue(selector);
                    },
                    set: function (newValue) {
                        setElementValue(selector, newValue);
                    },
                    configurable: true,
                    enumerable: (enumerable)
                });
            };
            return {
                bindModelToView
            };
        })();
    </script>
</head>
<body>
    <div style="padding: 20%;">
        <input  type="text" id="text" style="width: 40px;"/>
    </div>
    <script>
        let x = {a: 1, b: 2};
        dataBind.bindModelToView('#text', x, 'a'); //data to dom

        setInterval(function () {
             x.a++;
        }, 1000);
   </script> 
</body>

</html>

JSFiddle.

JSFiddle cu codul original.

În exemplul furnizat a proprietate a obiectului x actualizată de către setInterval și valoarea lui text input se actualizează și ea automat. În cazul în care nu este suficient și event este ceea ce căutați, puteți adăuga onchange listener la intrarea de mai sus. De asemenea, dacă este necesar, datele de intrare pot fi făcute ascunse.

TabsNotSpaces

Acesta este un fir vechi, dar m-am împiedicat de al doilea cel mai înalt răspuns (ascultători personalizați) în timp ce căutam o soluție folosind Angular. În timp ce soluția funcționează, angular are o modalitate mai bună construită pentru a rezolva acest lucru folosind @Output și emițătorii de evenimente. Plecând de la exemplul din răspunsul la ascultătorul personalizat:

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

Acest lucru va actualiza acum npe părinte de fiecare dată când se face clic pe butonul copil. Lucru stackblitz

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