Ouvinte para o valor da propriedade muda em um objeto Javascript
-
21-08-2019 - |
Pergunta
Indo através de documentação Javascript, eu achei as duas funções seguintes em um objeto Javascript parece interessante:
.watch
- Relógios para uma propriedade para ser atribuído um valor e executa uma função quando isso ocorre
.
.unwatch
-. Remove um conjunto watchpoint com o método do relógio
Exemplo de uso:
o = { p: 1 };
o.watch("p", function (id,oldval,newval) {
console.log("o." + id + " changed from " + oldval + " to " + newval)
return newval;
});
Sempre que alterar o valor da propriedade de "p", esta função é acionado.
o.p = 2; //logs: "o.p changed from 1 to 2"
Eu estou trabalhando em Javascript para os últimos anos e nunca usou essas funções.
Alguém por favor pode jogar alguns casos bom uso onde estas funções virá a calhar?
Solução
O relógio é realmente concebido para a validação dos valores de propriedade. Por exemplo, você pode validar que algo é um inteiro:
obj.watch('count', function(id, oldval, newval) {
var val = parseInt(newval, 10);
if(isNaN(val)) return oldval;
return val;
});
Você poderia usá-lo para comprimento da corda de validação:
obj.watch('name', function(id, oldval, newval) {
return newval.substr(0, 20);
});
No entanto, estas são apenas disponível nas versões mais recentes do motor SpiderMonkey JavaScript. Great se você estiver usando Jaxer ou incorporando o motor SpiderMonkey, mas não é realmente disponível no seu browser ainda (a menos que você é usando FF3).
Outras dicas
Confira Object.defineProperty
Object.prototype.\__defineGetter__
(ou \__defineSetter__
) para ver onde esta funcionalidade é rubrica.
Object.defineProperty
deve estar disponível em todos os navegadores contemporâneos real logo agora.
Agora é 2018 e as respostas para essa pergunta está um pouco desatualizado:
- Object.watch e Object.observe são ambos obsoleto e não deve ser usado.
- onPropertyChange é um evento de elemento DOM manipulador que só funciona em algumas versões do IE.
- Object.defineProperty permite que você faça uma imutável objeto imóvel, o que lhe permitiria detectar tentativas de mudanças, mas também iria bloquear quaisquer alterações.
- Defining setters e getters obras , mas requer um monte de código de configuração e ele não funciona bem quando você precisa excluir ou criar novas propriedades.
Hoje, agora você pode usar o Proxy objeto para monitor (e interceptar) as alterações feitas em um objeto. É construído propositadamente para o que o OP está tentando fazer. Aqui está um exemplo básico:
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'
Os únicos inconvenientes do objeto Proxy
são:
- O objeto
Proxy
não está disponível em navegadores mais antigos (como IE11) eo polyfill não pode funcionalidadeProxy
totalmente replicar.
objetos - proxy nem sempre se comportam como esperado com objetos especiais (por exemplo,
Date
) -. objetoProxy
é melhor emparelhado com objetos simples ou Arrays
Se você precisa observar alterações feitas em um objeto aninhado , então você precisa usar uma biblioteca, tal especializada como observable Magro (que eu autoria). Funciona assim:
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}}]
Você poderia dar uma olhada na biblioteca de Eventos Javascript Propery . É um pequeno estendendo Object.defineProperty
biblioteca com algumas chamadas de eventos, que fiz recentemente. Ele adiciona algumas propriedades on[event]
que podem ser utilizadas como as propriedades on[event]
de HTML-Objects. Ele também tem uma verificação de tipo simples, que chama o evento onerror
se ele falhar.
Como o código que resultaria em algo como isto:
var o = {}
Object.defineProperty(o, "p", {
value:1,
writable:true,
onchange:function(e){
console.log("o." + e.target + " changed from " + e.previousValue + " to " + e.returnValue);
}
})
Você pode usar setInterval
Object.prototype.startWatch = function (onWatch) {
var self = this;
if (!self.watchTask) {
self.oldValues = [];
for (var propName in self) {
self.oldValues[propName] = self[propName];
}
self.watchTask = setInterval(function () {
for (var propName in self) {
var propValue = self[propName];
if (typeof (propValue) != 'function') {
var oldValue = self.oldValues[propName];
if (propValue != oldValue) {
self.oldValues[propName] = propValue;
onWatch({ obj: self, propName: propName, oldValue: oldValue, newValue: propValue });
}
}
}
}, 1);
}
}
var o = { a: 1, b: 2 };
o.startWatch(function (e) {
console.log("property changed: " + e.propName);
console.log("old value: " + e.oldValue);
console.log("new value: " + e.newValue);
});
remover Promise e manter callback somente se Promessa não é suportado no seu browser alvo
Importante:
1) Esteja ciente de comportamento assíncrono em usar promessa.
2) Object.defineProperty não acionar o retorno de chamada, único operador de atribuição '=' faz
Object.onPropertySet = function onPropertySet(obj, prop, ...callback_or_once){
let callback, once;
for(let arg of callback_or_once){
switch(typeof arg){
case "function": callback = arg; break;
case "boolean": once = arg; break;
}
}
let inner_value = obj[prop];
let p = new Promise(resolve => Object.defineProperty(obj, prop, {
configurable: true,
// enumerable: true,
get(){ return inner_value; },
set(v){
inner_value = v;
if(once){
Object.defineProperty(obj, prop, {
configurable: true,
// enumerable: true,
value: v,
writable: true,
});
}
(callback || resolve)(v);
}
}));
if(!callback) return p;
};
// usage
let a = {};
function sayHiValue(v){ console.log(`Hi "${v}"`); return v; }
// do
Object.onPropertySet(a, "b", sayHiValue);
a.b = 2; // Hi "2"
a.b = 5; // Hi "5"
// or
Object.onPropertySet(a, "c", true).then(sayHiValue).then(v => {
console.log(a.c); // 4 // because a.c is set immediatly after a.c = 3
console.log(v); // 3 // very important: v != a.c if a.c is reassigned immediatly
a.c = 2; // property "c" of object "a" is re-assignable by '=' operator
console.log(a.c === 2); // true
});
a.c = 3; // Hi "3"
a.c = 4; // (Nothing)