À L'écoute des changements de variables dans JavaScript

est-il possible d'avoir un événement dans JS qui se déclenche lorsque la valeur d'une certaine variable change? JQuery est accepté.

344
demandé sur A-Sharabiani 2009-11-19 02:58:15

16 réponses

Oui, object.watch (ce n'est pas standard). Voici mon implémentation qui fonctionne dans chaque navigateur principal actuel.

186
répondu Eli Grey 2012-04-21 18:43:31

Oui, c'est maintenant tout à fait possible!

je sais que c'est un vieux fil, mais maintenant cet effet est possible en utilisant des accesseurs (getters et setters): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters

vous pouvez définir un objet comme ceci, dans lequel aInternal représente le champ 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;
  }
}

alors vous pouvez enregistrer un auditeur en utilisant le code suivant:

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

ainsi chaque fois que quelque chose change la valeur de x.a , la fonction d'écoute sera allumée. L'exécution de la ligne suivante apportera le popup d'alerte:

x.a = 42;

voir un exemple ici: https://jsfiddle.net/5o1wf1bn/1 /

, Vous pouvez également l'utilisateur d'un tableau d'auditeurs, au lieu d'un seul écouteur connecteur, mais je voulais vous donner le plus simple exemple possible.

43
répondu Akira 2016-05-24 01:46:20

Pas de.

mais, si c'est vraiment si important, vous avez 2 options (la première est testée, la seconde ne l'est pas):

D'abord, utilisez des setters et des getters, comme ceci:

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

, alors vous pouvez faire quelque chose comme:

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

puis définissez l'auditeur comme:

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

Deuxième, j'ai effectivement oublié, je vais soumettre pendant que j'y pense :)

EDIT: Oh, I rappelez-vous :) Vous pouvez mettre une montre sur la valeur:

donné myobj ci-dessus, avec " a "dessus:

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);
34
répondu Luke Schafer 2009-11-19 00:33:25

désolé de soulever un vieux fil, Mais voici un petit manuel pour ceux qui (comme moi!) ne voyez pas comment fonctionne L'exemple D'Eli Grey:

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

j'Espère que cela peut aider quelqu'un

23
répondu javascript is future 2016-03-28 18:08:46

par 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>

autre exemple

// 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>
20
répondu Eduardo Cuomo 2016-11-04 13:00:27

la plupart des réponses à cette question sont soit périmées, inefficaces, ou nécessitent l'inclusion de grandes bibliothèques gonflées:

  • objet.regarder et objet.observez que est à la fois déprécié et ne doit pas être utilisé.
  • onPropertyChange est un gestionnaire D'événements DOM element qui ne fonctionne que dans certaines versions D'IE.
  • Objet.defineProperty vous permet de rendre une propriété d'objet immuable, ce qui vous permettrait de détecter les modifications tentées, mais cela bloquerait également toutes les modifications.
  • définir setters et getters fonctionne, mais il nécessite beaucoup de code de configuration et il ne fonctionne pas bien lorsque vous avez besoin de supprimer ou de créer de nouvelles propriétés.

Aujourd'hui, vous pouvez maintenant utiliser le Proxy objet pour surveiller (et intercepter) les changements apportés à un objet. Il est conçu pour ce que L'OP essaie de faire. Voici un exemple de base:

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'

les seuls inconvénients de l'objet Proxy sont:

  1. l'objet Proxy n'est pas disponible dans les navigateurs plus anciens (tels que IE11) et le polyfill ne peut pas répliquer complètement la fonctionnalité Proxy .
  2. Proxy les objets ne se comportent pas toujours comme prévu avec des objets spéciaux (p. ex. Date ) -- l'objet Proxy est le mieux apparié avec des objets simples ou des tableaux.

si vous devez observer les changements apportés à un objet imbriqué , alors vous devez utiliser une bibliothèque spécialisée comme Slim Observable qui fonctionne comme ceci:

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}}]
10
répondu Elliot B. 2018-08-07 03:36:47

comme réponse de Luke Schafer ( note : ceci se réfère à son post original; mais tout le point ici reste valide après l'édition), je suggérerais également une paire de méthodes Get/Set pour accéder à votre valeur.

cependant, je suggère quelques modifications (et c'est pourquoi je poste...).

un problème avec ce code est que le champ a de l'objet myobj est directement accessible, il est donc possible d'y accéder / changer sa valeur sans déclencher les auditeurs:

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!

Encapsulation

donc, pour garantir que les auditeurs sont effectivement appelés, nous devrions interdire cet accès direct au champ a . Comment faire? Utilisez un fermeture !

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

Maintenant, vous pouvez utiliser la même méthode pour créer et ajouter l' auditeurs comme Luke l'a proposé, mais vous pouvez être sûr qu'il n'y a aucun moyen possible de lire ou d'écrire à a passer inaperçu!

ajouter des champs encapsulés par programmation

toujours sur la piste de Luc, je propose maintenant une manière simple d'ajouter des champs encapsulés et les getters/setters respectifs aux objets au moyen d'un simple appel de fonction.

Notez que cela ne fonctionnera correctement qu'avec valeur types de . Pour que cela fonctionne avec types de référence , une sorte de copie profonde devrait être mis en œuvre (Voir celle-ci , par exemple).

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

cela fonctionne comme avant: nous créons une variable locale sur une fonction, puis nous créons une fermeture.

Comment l'utiliser? Simple:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
7
répondu Bruno Reis 2017-05-23 11:47:30

AngularJS (je sais que ce n'est pas JQuery , mais cela pourrait aider. [Pure JS est bon en théorie seulement]):

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

où "données" est le nom de votre variable dans la portée.

il y a un lien vers doc.

6
répondu ses 2013-12-12 14:38:53

si vous utilisez jQuery {UI} (que tout le monde devrait utiliser :-) ), vous pouvez utiliser .modifier() avec une cachée .

5
répondu Chuck Han 2011-05-25 00:37:44

Pour ceux de réglage dans une couple d'années plus tard:

une solution pour la plupart des navigateurs (et IE6+) est disponible qui utilise l'événement onpropertychange et la nouvelle spécification defineProperty. Le petit problème est que vous aurez besoin de faire de votre variable un objet dom.

tous les détails:

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

5
répondu MandoMando 2012-06-06 15:05:29

pas directement: Vous avez besoin d'une paire getter/setter avec une interface" addListener/removeListener " d'une sorte... ou un plugin NPAPI (mais c'est une autre histoire).

2
répondu jldupont 2009-11-19 00:06:42

la fonctionnalité que vous recherchez peut être obtenue en utilisant la méthode "defineProperty()" qui n'est disponible que pour les navigateurs modernes:

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

j'ai écrit une extension jQuery qui a une fonctionnalité similaire si vous avez besoin de plus de soutien de navigateur croisé:

https://github.com/jarederaj/jQueue

une petite extension jQuery qui gère les appels de file d'attente vers le l'existence d'une variable, d'un objet ou d'une clé. Vous pouvez assigner n'importe quel nombre de les rappels à n'importe quel nombre de points de données qui pourraient être affectées par processus en cours d'exécution en arrière-plan. jQueue écoute et attend ces données que vous spécifiez pour venir dans l'existence et puis déclenche le corrigez le rappel avec ses arguments.

2
répondu jarederaj 2014-03-08 16:45:11
//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();
    }
};

ou

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

ou

/* 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);
1
répondu Avatar 2015-02-11 05:50:09

une solution assez simple et simpliste est d'utiliser simplement un appel de fonction pour définir la valeur de la variable globale, et ne jamais définir sa valeur directement. De cette façon, vous avez le contrôle total:

var globalVar;

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

il n'y a aucun moyen de faire respecter cela, cela nécessite juste de la discipline de programmation... bien que vous puissiez utiliser grep (ou quelque chose de similaire) pour vérifier que nulle part votre code ne définit directement la valeur de globalVar .

Ou vous pouvez encapsuler dans un objet et l'utilisateur getter et setter... juste une pensée.

0
répondu markvgti 2015-09-24 05:43:58

ce n'est pas directement possible.

cependant, cela peut être fait en utilisant CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent

La méthode ci-dessous accepte un tableau de noms de variables en entrée et ajoute écouteur d'événement pour chaque variable et déclenche l'événement pour toute modification de la valeur des variables.

la méthode utilise le sondage pour détecter le changement valeur. Vous pouvez augmenter la valeur de délai d'attente en millisecondes.

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

en appelant la méthode:

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

il détectera les changements de variables username et userid.

0
répondu Ketan Yekale 2018-06-28 05:58:56
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]);}
                    });
            }
-2
répondu Avatar 2015-02-11 06:58:07