簡體   English   中英

如何觀察通過setter-getter或Proxy公開的對象的array屬性內容的更改

[英]How to observe changes to contents of an object's array property exposed through setter-getter or Proxy

使用getter / setter

我正在創建如下的IIFE。 它將getter和setter返回到內部存儲的數組變量。 我希望攔截對該數組所做的更改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()

尋找有關如何攔截這些內容更改的任何想法。

使用代理

這是使用新的Proxy對象的替代嘗試:

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[0] = 1a.push(3)任何內容觀察對a.arr內容所做的更改。

更新:優雅的解決方案(由Kris Pruden和Sindre Sorhus提供)

解決方案基於Kris在Sindre的“ on-change”庫中的 提交

解決方案的解釋 ,由Kris撰寫:

set陷阱中,我的目標是確定所提供的值是否是先前調用get陷阱所產生的Proxy 如果是這樣的Proxy ,則任何屬性訪問都將被我們自己的get陷阱攔截。 因此,當我訪問value[proxyTarget]將調用我們的get陷阱,當property === proxyTarget時,該陷阱將被編碼為返回target (第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);
};

這解決了我的問題,而無需借助檢查數組修改方法的技巧。


原始解決方案:

David Walsh在這里的帖子的幫助下,到目前為止,我已經對此進行了排序。 它仍然很難看,但是現在可以使用。

更新了具有遞歸式get陷阱的onChanged Proxy maker。

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

還添加了用於檢查獲取陷阱的函數列表(丑陋,hacky):

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

到目前為止,這似乎在我的測試中起作用。

感謝您指出正確的方向。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM