如何订阅对象更改?

时间:2018-04-25 11:20:27

标签: javascript

我想知道如何订阅JavaScript对象的更改,例如像Redux一样。我阅读了很多JS文档,但是我找不到一种不推荐的方法来处理这个问题(Object.protype.watch()以及Object.observe()已被弃用)。此外,我阅读了有关此主题的一些Stackoverflow问题,但它们都至少有5年的历史。为了可视化我的问题,我将展示一个例子。

这可能是我想要关注的对象:

const store = {
    anArray = [
    'Hi',
    'my',
    'name',
    'is'
  ]
}

..这是一个改变store对象的函数:

function addAName() {
    store.anArray.push('Bob')
}

此示例中的目标是每次store对象更改时触发以下函数

function storeChanged() {
    console.log('The store object has changed!')
}

提前谢谢!

7 个答案:

答案 0 :(得分:3)

您是否尝试过使用ECMA6中的代理?我想这就是你要找的东西 您只需要将一个函数定义为Proxy的验证器集,如下所示:

let validator = {
    set: function(target, key, value) {
        console.log(`The property ${key} has been updated with ${value}`);
        return true;
    }
};
let store = new Proxy({}, validator);
store.a = 'hello';
// console => The property a has been updated with hello

答案 1 :(得分:1)

这不重要。

有几种方法可以使用不同的工具(Redux,Angular,KnockoutJS等)。

  1. 通过功能引导更改 - 这是Redux使用的方法(more)。你没有直接修改东西,你通过reducer传递它们,这意味着Redux意识到你已经改变了一些东西。

  2. 差异 - 直接将对象树与对象树的先前副本进行比较,然后根据所做的更改进行操作。至少某些版本的Angular / AngularJS使用(d)这种方法。

  3. 环绕 - (#1上的一种变体)使用包装器对树中的所有对象(例如阵列上的push方法)进行所有修改操作通过包装这些方法(并用访问器属性替换简单数据属性)和/或使用Proxy对象来通知控制器它们所在的对象已被调用。 KnockoutJS使用这种方法的一种版本。

答案 2 :(得分:0)

我不确定是否有办法使用本机功能,即使有,我认为如果没有某种抽象,你将无法做到这一点。

它甚至不能在react / redux中本地工作,因为您需要明确地专门调用setState来触发更改。我推荐一种观察者模式,其实现大致如此。

var store = {
  names: ['Bob', 'Jon', 'Doe']
}

var storeObservers = [];

现在,简单地推动你的观察者功能无关紧要,即使它们是某个组件或模块的一部分

storeObservers.push(MyComponent.handleStoreChange)

现在简单地公开一个函数来更改类似于setState

的商店
function changeStore(store) {
  store = store
  storeObservers.forEach(function(observer){
    observer()
  })
}

显然,您可以将其模块化以处理更复杂的情况,或者您只想更改状态的一部分并允许观察者将回调绑定到部分状态更改。

答案 3 :(得分:0)

要在没有任何间接(使用对象)的情况下解决此问题,您可以使用代理。

通过使用observalbe包装所有对象,您可以自由编辑商店,并_base跟踪哪个属性已更改。



const observalbe = (target, callback, _base = []) => {
  for (const key in target) {
    if (typeof target[key] === 'object')
      target[key] = observalbe(target[key], callback, [..._base, key])
  }
  return new Proxy(target, {
    set(target, key, value) {
      if (typeof value === 'object') value = observalbe(value, callback, [..._base, key])
      callback([..._base, key], target[key] = value)
      return value
    }
  })
}

const a = observalbe({
  a: [1, 2, 3],
  b: { c: { d: 1 } }
}, (key, val) => {
  console.log(key, val);
})

a.a.push(1)

a.b.c.d = 1
a.b = {}
a.b.c = 1




答案 4 :(得分:0)

您可以使用Object.defineproperty()创建反应式getter / setter。它具有良好的浏览器支持,看起来很方便。



function Store() {

  let array = [];

  Object.defineProperty(this, 'array', {
    get: function() {
      console.log('Get:', array);
      return array;
    },
    set: function(value) {
      array = value;
      console.log('Set:', array)
    }
  });


}

var store = new Store();
store.array; //Get: []
store.array = [11]; //Set: [11]
store.array.push(5) //Set: [11, 5]
store.array = store.array.concat(1, 2, 3) //Set: [11, 5, 1, 2, 3]




答案 5 :(得分:0)

在大多数情况下,使用Proxy

Alejandro Riera's answer可能是正确的方法。但是,如果您愿意包含一个(小巧的〜90kb)库,则Vue.js的实例上可以包含watched properties,可以满足您的要求。

仅将其用于更改观察可能是矫kill过正,但是,如果您的网站还有其他用于反应式框架的用途,那么这可能是个好方法。

我有时将其用作没有相关元素的对象存储,例如:

const store = new Vue({
    data: {
        anArray: [
            'Hi',
            'my',
            'name',
            'is'
        ]
    },
    watch: {
        // whenever anArray changes, this function will run
        anArray: function () {
            console.log('The store object has changed:', this.anArray);
        }
    }
});

function addAName() {
    // push random strings as names
    const newName = '_' + Math.random().toString(36).substr(2, 6);
    store.anArray.push(newName);
}

// demo
setInterval(addAName, 5000);

答案 6 :(得分:0)

你可以试试这个 npm 包 tahasoft-event-emitter

你的代码看起来像这样

import { EventEmitter } from "tahasoft-event-emitter";

const store = {
  anArray: ["Hi", "my", "name", "is"]
};

const onStoreChange = new EventEmitter();

function addAName(name) {
  onStoreChange.emit(name);
  store.anArray.push(name);
}

function storeChanged(name) {
  console.log("The store object has changed!. New name is " + name);
}

onStoreChange.add((name) => storeChanged(name));

addAName("Bob");

如果你对 name 的新值不感兴趣,可以这么简单写

import { EventEmitter } from "tahasoft-event-emitter";

const store = {
  anArray: ["Hi", "my", "name", "is"]
};

const onStoreChange = new EventEmitter();

function addAName() {
  store.anArray.push("Bob");
  onStoreChange.emit();
}

function storeChanged() {
  console.log("The store object has changed!");
}

onStoreChange.add(storeChanged);

addAName();

每当您调用 addAName 时,都会调用 storeChanged

这是关于代码和框的示例 EventEmitter on an object status change

您可以检查包 GitHub 存储库以了解它是如何创建的。很简单。