Почему Object.observe() устарел
Существует ли альтернативный подход?
Есть ли другой способ сделать обнаружение изменений в объекте?
Существует метод прокси, но может ли кто-нибудь сказать мне, как я могу добиться этого с помощью прокси:
var obj = {
foo: 0,
bar: 1
};
Object.observe(obj, function(changes) {
console.log(changes);
});
obj.baz = 2;
// [{name: 'baz', object: <obj>, type: 'add'}]
obj.foo = 'hello';
// [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]
Ответы
Ответ 1
Вы можете достичь этого с помощью геттеров и сеттеров.
var obj = {
get foo() {
console.log({ name: 'foo', object: obj, type: 'get' });
return obj._foo;
},
set bar(val) {
console.log({ name: 'bar', object: obj, type: 'set', oldValue: obj._bar });
return obj._bar = val;
}
};
obj.bar = 2;
// {name: 'bar', object: <obj>, type: 'set', oldValue: undefined}
obj.foo;
// {name: 'foo', object: <obj>, type: 'get'}
Кроме того, в браузере с поддержкой прокси вы можете написать более общее решение.
var obj = {
foo: 1,
bar: 2
};
var proxied = new Proxy(obj, {
get: function(target, prop) {
console.log({ type: 'get', target, prop });
return Reflect.get(target, prop);
},
set: function(target, prop, value) {
console.log({ type: 'set', target, prop, value });
return Reflect.set(target, prop, value);
}
});
proxied.bar = 2;
// {type: 'set', target: <obj>, prop: 'bar', value: 2}
proxied.foo;
// {type: 'get', target: <obj>, prop: 'bar'}
Ответ 2
Решение @Dan Prince правильное. Это должен быть первый выбор.
В случае, если вы хотите поддерживать более старые браузеры, я бы предложил вам использовать polyfill или использовать Object.defineProperties API, который поддерживается в IE 9.
var obj = Object.defineProperties({}, {
"foo":{
get:function(){
console.log("Get:"+this.value);
},
set:function(val){
console.log("Set:"+val);
this.value = val;
}
},
"bar":{
get:function(){
console.log("Get:"+this.value);
},
set:function(val){
console.log("Set:"+val);
this.value = val;
}
}
});
Ответ 3
Отказ от ответственности: я являюсь автором object-observer, предложенной ниже.
Я бы не пошел с решением getters/seters - он был сложным, не масштабируемым и не поддерживаемым. Магистраль сделала свое двустороннее связывание таким образом, и шаблон, чтобы заставить его работать правильно, был довольно фрагментом кода.
Прокси - лучший способ добиться того, что вам нужно, просто добавьте к примерам выше регистрацию и управление обратными вызовами и выполните их при изменениях.
Что касается библиотек polyfill: некоторые/большинство из них реализованы с использованием "грязной проверки" или техники опроса - неэффективны, но не работают. Иногда это относится к polyfill, указанному выше Nirus.
Я бы рекомендовал забрать некоторую библиотеку, которая выполняет наблюдение через Proxies. Есть несколько, object-observer, являющийся одним из них: написанный для этого прецедента точно, использует собственные Proxies, обеспечивает глубокое дерево наблюдения и т.д.