Прослушиватель изменений значений свойств в объекте Javascript

StackOverflow https://stackoverflow.com/questions/1063813

  •  21-08-2019
  •  | 
  •  

Вопрос

Просматривая документацию Javascript, я обнаружил, что следующие две функции объекта Javascript выглядят интересно:

.watch - Отслеживает присвоение значения свойству и запускает функцию, когда это происходит.
.unwatch — Удаляет точку наблюдения, установленную с помощью метода watch.

Пример использования:

o = { p: 1 };
o.watch("p", function (id,oldval,newval) {
    console.log("o." + id + " changed from " + oldval + " to " + newval)
    return newval;
});

Всякий раз, когда мы меняем значение свойства «p», эта функция срабатывает.

o.p = 2;   //logs: "o.p changed from 1 to 2"

Я работаю над Javascript последние несколько лет и никогда не использовал эти функции.
Может кто-нибудь подсказать несколько хороших случаев использования, где эти функции пригодятся?

Это было полезно?

Решение

На самом деле часы предназначены для проверки значений свойств.Например, вы можете проверить, что что-то является целым числом:

obj.watch('count', function(id, oldval, newval) {
    var val = parseInt(newval, 10);
    if(isNaN(val)) return oldval;
    return val;
});

Вы можете использовать его для проверки длины строки:

obj.watch('name', function(id, oldval, newval) {
    return newval.substr(0, 20);
});

Однако они доступны только в последних версиях JavaScript-движка SpiderMonkey. Отлично, если вы используете Jaxer или встраиваете движок SpiderMonkey, но он еще не доступен в вашем браузере (если вы не используете FF3).

Другие советы

Проверить Object.defineProperty иObject.prototype.\__defineGetter__ (или \__defineSetter__ ), чтобы увидеть, куда движется эта функциональность.

Object.defineProperty Очень скоро он должен быть доступен во всех современных браузерах.

Сейчас 2018 год, и ответы на этот вопрос немного устарели:

  • Объект.смотреть и Объект.наблюдать оба устарели и не должны использоваться.
  • onPropertyChange — это обработчик событий элемента DOM, который работает только в некоторых версиях IE.
  • Object.defineProperty позволяет сделать свойство объекта неизменяемым, что позволит обнаруживать попытки внесения изменений, но также блокирует любые изменения.
  • Определение сеттеров и геттеров работает, но требует большого количества кода настройки и не работает, когда вам нужно удалить или создать новые свойства.

Сегодня, теперь вы можете использовать Прокси объект отслеживать (и перехватывать) изменения, внесенные в объект.Он специально создан для того, что пытается сделать ОП.Вот базовый пример:

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'

Единственные недостатки этого Proxy объект:

  1. А Proxy объект недоступен в старых браузерах (например, IE11), и полифилл не могу полностью повторить Proxy функциональность.
  2. Прокси-объекты не всегда ведут себя должным образом со специальными объектами (например, Date) -- Proxy объект лучше всего сочетается с простыми объектами или массивами.

Если вам необходимо наблюдать за изменениями, внесенными в вложенный объект, то вам нужно использовать специализированную библиотеку, например Наблюдаемый тонкий (который я написал).Это работает следующим образом:

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}}]

Вы могли бы взглянуть на События собственности Javascript библиотека.Это небольшая библиотека, расширяющаяся Object.defineProperty с некоторыми абонентами событий, которые я сделал недавно.Это добавляет несколько on[event] свойства, которые можно использовать как on[event] свойства HTML-объектов.Он также имеет простую проверку типа, которая вызывает метод onerror событие, если оно не удалось.

Взяв ваш код, это приведет к чему-то вроде этого:

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

Вы можете использовать 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);
});

Object.defineProperty

Обещать

удалить Promise и сохранить обратный вызов, только если Promise не поддерживается в вашем целевом браузере

Важный:

1) Помните об асинхронном поведении при использовании обещания.

2) Object.defineProperty не запускает обратный вызов, работает только оператор назначения '='

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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top