Как наблюдать изменения в содержании свойства массива объекта, выставленного через setter-getter или Proxy - PullRequest
0 голосов
/ 28 февраля 2019

Использование getter / setter

Я создаю IIFE, как показано ниже.Он возвращает геттеры и сеттеры в переменную массива, хранящуюся внутри.Я хочу перехватить изменения, внесенные в этот массив - console.log предназначен для указания этого в установщике ниже.

const a = (function() {
  let arr = [];

  return {
      get arr() {return arr},
      set arr(v) {
          console.log("new arr", v);
          arr = v;
      },
  }
})();

Это прекрасно работает, если я полностью переназначу arr, например, a.arr = [1, 2].

Но он не перехватывает изменения, внесенные в содержимое массива, например, a.arr.push(3) или a.arr.shift().

Поиск любых идей о том, как перехватить эти изменения содержимого.

Использование прокси

Это альтернативная попытка использования нового объекта прокси:

a = (function() {

    let details = {
        arr: []
    }

    function onChangeProxy(object, onChange) {
        const handler = {
            apply: function (target, thisArg, argumentsList) {
                onChange(thisArg, argumentsList);
                return thisArg[target].apply(this, argumentsList);
            },
            defineProperty: function (target, property, descriptor) {
                Reflect.defineProperty(target, property, descriptor);
                onChange(property, descriptor);
                return true;
            },
            deleteProperty: function(target, property) {
                Reflect.deleteProperty(target, property);
                onChange(property, descriptor);
                return;
            }
        };

        return new Proxy(object, handler);
    };

    return onChangeProxy(details, (p, d) => console.log(p, d));

})();

Проблема остается той же.По-прежнему невозможно наблюдать изменения, внесенные в содержимое a.arr, используя что-либо от a.arr[0] = 1 до a.push(3).

1 Ответ

0 голосов
/ 28 февраля 2019

Обновление: элегантное решение (любезно предоставлено Крисом Пруденом и Синдром Сорхусом)

Решение основано на этом коммите Криса в библиотеке 'on-change' Синдре .

Объяснение решения , Крис:

В ловушке set моя цель - определить, является ли предоставленное значение Proxy произведено предыдущим вызовом ловушки get.Если это Proxy, то любой доступ к свойству будет перехвачен нашей собственной get ловушкой.Поэтому, когда я получу доступ к value[proxyTarget], будет вызвана наша get ловушка, которая закодирована так, чтобы возвращать target при property === proxyTarget (строка 46).Если значение, переданное set, не является прокси-сервером, созданным при изменении, то value[proxyTarget] - это undefined.

Полный код решения:

(object, onChange) => {
    let inApply = false;
    let changed = false;

    function handleChange() {
        if (!inApply) {
            onChange();
        } else if (!changed) {
            changed = true;
        }
    }

    const handler = {
        get(target, property, receiver) {
            const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
            const value = Reflect.get(target, property, receiver);

            // Preserve invariants
            if (descriptor && !descriptor.configurable) {
                if (descriptor.set && !descriptor.get) {
                    return undefined;
                }
                if (descriptor.writable === false) {
                    return value;
                }
            }

            try {
                return new Proxy(value, handler);
            } catch (_) {
                return value;
            }
        },
        set(target, property, value) {
            const result = Reflect.set(target, property, value);

            handleChange();

            return result;
        },
        defineProperty(target, property, descriptor) {
            const result = Reflect.defineProperty(target, property, descriptor);

            handleChange();

            return result;
        },
        deleteProperty(target, property) {
            const result = Reflect.deleteProperty(target, property);

            handleChange();

            return result;
        },
        apply(target, thisArg, argumentsList) {
            if (!inApply) {
                inApply = true;
                const result = Reflect.apply(target, thisArg, argumentsList);
                if (changed) {
                    onChange();
                }
                inApply = false;
                changed = false;
                return result;
            }

            return Reflect.apply(target, thisArg, argumentsList);
        }
    };

    return new Proxy(object, handler);
};

Это решило мою проблему, не прибегая к хакерской проверке методов изменения массива.


Исходное решение:

Я уже разобрался с помощью Пост Дэвида Уолша здесь .Это все еще безобразно, но работает на данный момент.

Обновлен onChanged Proxy Maker с рекурсивным прерыванием get.

get: function (target, property, receiver) {
    let retval;

    try {
        retval = new Proxy(target[property], handler);
    } catch (err) {
        retval = Reflect.get(target, property, receiver);
    }

    if (mutators.includes(property))
        onChange(target, property, receiver);

    return retval;
},

Также добавлен список функций для проверкиполучить ловушку против (уродливого, хакерского бита):

const mutators = [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "reverse",
    "fill",
    "sort"
]

Это, кажется, работает в моем тестировании до сих пор.

Спасибо за указание в правильном направлении.

...