我想知道如何订阅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!')
}
提前谢谢!
答案 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等)。
答案 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 存储库以了解它是如何创建的。很简单。