555 votes

Écouter les changements de variables en JavaScript

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

0 votes

@BenjaminGruenbaum Vous voulez probablement dire MutableObserver (pour DOM). Object est seulement pour les objets JS de ce que je me rappelle.

4 votes

@HellBaby cette question concerne les variables - pas le DOM.

10 votes

@BenjaminGruenbaum d'après developer.mozilla.org/fr/US/docs/Web/JavaScript/Référence/ Object.observe est obsolète ou déprécié. Le remplacement recommandé (selon cette même page) est l'objet Proxy.

191voto

Eli Grey Points 17553

Oui, object.watch (il s'agit toutefois d'un système non standard). Voici ma mise en œuvre qui fonctionne dans tous les principaux navigateurs actuels.

35voto

Luke Schafer Points 6250

Non.

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

Tout d'abord, utilisez les setters et les getters, comme suit :

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éfinir l'écouteur comme :

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

Deuxièmement, vous pourriez mettre une montre sur la valeur :

Étant donné mon objet ci-dessus, avec un '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);

15 votes

"surveiller" ne signifie pas réellement détecter un changement. Ce n'est pas basé sur des événements et cela ralentirait certainement l'ensemble de l'application très très vite. À mon avis, ces approches ne devraient jamais faire partie d'un véritable projet.

22voto

Désolé de faire remonter un vieux fil, mais voici un petit manuel pour ceux qui (comme moi !) ne voient 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 pourra aider quelqu'un

10 votes

La montre n'est pas prise en charge sur Chrome ou Safari pour le moment, uniquement sur Firefox.

5 votes

Selon le réseau de développeurs Mozilla, ce n'est pas recommandé. Object.prototype.watch() est destiné à des fins de test uniquement et ne doit pas être utilisé dans du code de production. developer.mozilla.org/fr/US/docs/Web/JavaScript/Référence/

4 votes

@PaulMcClean cette réponse était en réponse à la réponse d'Eli Grey qui contenait un Polyfill. gist.github.com/eligrey/384583

9voto

Bruno Reis Points 16132

Como La réponse de Luke Schafer ( note : ceci fait référence à son message original ; mais l'ensemble de l'argument reste valable après la modification. ), je suggère également une paire de méthodes Get/Set pour accéder à votre valeur.

Cependant, je suggérerais quelques modifications (et c'est pour cela que 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 / de modifier sa valeur sans déclencher les listeners :

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

Ainsi, pour garantir que les écouteurs 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 les écouteurs comme Luke l'a proposé, mais vous pouvez être sûr qu'il n'y a aucun moyen de lire ou d'écrire dans le fichier a qui passent inaperçus !

Ajouter des champs encapsulés de manière programmatique

Toujours sur la piste de Luke, je propose maintenant un moyen 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 types de valeurs . Pour que cela fonctionne avec types de référence une sorte de copie profonde devrait être mis en œuvre (voir celui-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 de la même manière que précédemment : nous créons une variable locale sur une fonction, puis nous créons une fermeture.

Comment l'utiliser ? C'est simple :

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

2 votes

+1 pour l'encapsulation. C'était ma première idée, mais je voulais avoir la possibilité d'ajouter la méthode create_gets_sets par la suite, et comme elle est indiscriminée, cacher les valeurs n'est pas cool :) on peut aller plus loin et écrire des choses pour cacher les valeurs, mais je pense que le code que j'ai posté est assez confus pour la plupart des gens... peut-être si il y a une demande pour ça...

7voto

Chuck Han Points 304

Si vous utilisez jQuery {UI} (que tout le monde devrait utiliser :-) ), vous pouvez utiliser .change() avec un élément <input/> caché.

7 votes

Je ne comprends pas bien. Comment pouvez-vous attacher une variable à un objet caché <input/> élément ?

4 votes

Je pense que Chuck suggère que vous pouvez définir la valeur de l'entrée à l'aide de jquery, puis d'un écouteur d'événements .change(). Si vous mettez à jour la valeur de l'entrée avec .val(), le rappel de l'événement .change() se déclenchera.

2 votes

<input type="hidden" value="" id="thisOne" /> et avec jQuery $("#thisOne").change(function() { do stuff here }); y $("#thisOne").val(myVariableWhichIsNew); et ensuite le .change() fera feu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X