À 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é.
16 réponses
Oui, object.watch
(ce n'est pas standard). Voici mon implémentation qui fonctionne dans chaque navigateur principal actuel.
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.
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);
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
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>
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:
- 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
. - Proxy les objets ne se comportent pas toujours comme prévu avec des objets spéciaux (p. ex.
Date
) -- l'objetProxy
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}}]
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);
si vous utilisez jQuery {UI} (que tout le monde devrait utiliser :-) ), vous pouvez utiliser .modifier() avec une cachée .
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 /
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).
la fonctionnalité que vous recherchez peut être obtenue en utilisant la méthode "defineProperty()" qui n'est disponible que pour les navigateurs modernes:
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.
//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);
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.
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.
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]);}
});
}