使用 Proxy 对象检测 Javascript 数组中的更改

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35610242/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 17:59:01  来源:igfitidea点击:

Detecting changes in a Javascript array using the Proxy object

javascript

提问by w2olves

It is relatively trivial to watch for changes in an array in Javascript.

在 Javascript 中观察数组的变化是相对简单的。

One method I use is like this:

我使用的一种方法是这样的:

// subscribe to add, update, delete, and splice changes
Array.observe(viewHelpFiles, function(changes) {
  // handle changes... in this case, we'll just log them 
  changes.forEach(function(change) {
    console.log(Object.keys(change).reduce(function(p, c) {
      if (c !== "object" && c in change) {
        p.push(c + ": " + JSON.stringify(change[c]));
      }
      return p;
    }, []).join(", "));
  });
});

However, I have recently read that Array.observeis deprecated and we should use the proxy object instead.

但是,我最近读到它Array.observe已被弃用,我们应该改用代理对象。

How can we detect changes in an array the Proxy object? I am unable to find any examples, anyone interested in elaborating?

我们如何检测 Proxy 对象数组的变化?我找不到任何例子,有人有兴趣详细说明吗?

回答by Icepickle

From what I can read from the MDN page, you can create a general handler where you can handle all the changes to any object.

从我可以从MDN 页面读取的内容来看,您可以创建一个通用处理程序,您可以在其中处理对任何对象的所有更改。

In a sense, you write an interceptor, that will intervene each time you get a value from the array or set a value. You can then write your own logic to follow the changes.

从某种意义上说,您编写了一个拦截器,它会在您每次从数组中获取值或设置值时进行干预。然后,您可以编写自己的逻辑来跟踪更改。

var arrayChangeHandler = {
  get: function(target, property) {
    console.log('getting ' + property + ' for ' + target);
    // property is index in this case
    return target[property];
  },
  set: function(target, property, value, receiver) {
    console.log('setting ' + property + ' for ' + target + ' with value ' + value);
    target[property] = value;
    // you have to return true to accept the changes
    return true;
  }
};

var originalArray = [];
var proxyToArray = new Proxy( originalArray, arrayChangeHandler );

proxyToArray.push('Test');
console.log(proxyToArray[0]);

// pushing to the original array won't go through the proxy methods
originalArray.push('test2');

// the will however contain the same data, 
// as the items get added to the referenced array
console.log('Both proxy and original array have the same content? ' 
  + (proxyToArray.join(',') === originalArray.join(',')));

// expect false here, as strict equality is incorrect
console.log('They strict equal to eachother? ' + (proxyToArray === originalArray));

Which then outputs:

然后输出:

getting push for 
getting length for 
setting 0 for  with value Test 
setting length for Test with value 1
getting 0 for Test
Test

The caveat for the proxy, is that everything which is defined on an object, will be intercepted, which can be observed when using the pushmethod.

代理的警告是,在对象上定义的所有内容都将被拦截,这可以在使用该push方法时观察到。

The original object that will be proxied doesn't mutate, and changes done to the original object will not be caught by the proxy.

将被代理的原始对象不会发生变异,并且对原始对象所做的更改不会被代理捕获。

回答by coderek

You can do somthing like this

你可以做这样的事情

new Proxy([], {
    get(target, prop) {
        const val = target[prop];
        if (typeof val === 'function') {
            if (['push', 'unshift'].includes(prop)) {
                return function (el) {
                    console.log('this is a array modification');
                    return Array.prototype[prop].apply(target, arguments);
                }
            }
            if (['pop'].includes(prop)) {
                return function () {
                    const el = Array.prototype[prop].apply(target, arguments);
                    console.log('this is a array modification');
                    return el;
                }
            }
            return val.bind(target);
        }
        return val;
    }
});