[英]Making objects observable
I've been looking into JavaScript frameworks such as Angular and Meteor lately, and I was wondering how they know when an object property has changed so that they could update the DOM. 最近,我一直在研究Angular和Meteor等JavaScript框架,我想知道他们如何知道何时更改了对象属性,以便可以更新DOM。
I was a bit surprised that Angular used plain old JS objects rather than requiring you to call some kind of getter/setter so that it could hook in and do the necessary updates. Angular使用普通的旧JS对象而不是要求您调用某种getter / setter,以便它可以挂接并进行必要的更新,这让我感到有些惊讶。 My understanding is that they just poll the objects regularly for changes.
我的理解是,它们只是定期轮询对象以进行更改。
But with the advent of getters and setters in JS 1.8.5 , we can do better than that, can't we? 但是随着JS 1.8.5中getter和setter方法的出现,我们可以做得更好,不是吗?
As a little proof-of-concept, I put together this script: 作为一点概念验证,我整理了以下脚本:
( Edit: updated code to add dependent-property/method support) ( 编辑:更新代码以添加从属属性/方法支持)
function dependentProperty(callback, deps) {
callback.__dependencies__ = deps;
return callback;
}
var person = {
firstName: 'Ryan',
lastName: 'Gosling',
fullName: dependentProperty(function() {
return person.firstName + ' ' + person.lastName;
}, ['firstName','lastName'])
};
function observable(obj) {
if (!obj.__properties__) Object.defineProperty(obj, '__properties__', {
__proto__: null,
configurable: false,
enumerable: false,
value: {},
writable: false
});
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if(!obj.__properties__[prop]) obj.__properties__[prop] = {
value: null,
dependents: {},
listeners: []
};
if(obj[prop].__dependencies__) {
for(var i=0; i<obj[prop].__dependencies__.length; ++i) {
obj.__properties__[obj[prop].__dependencies__[i]].dependents[prop] = true;
}
delete obj[prop].__dependencies__;
}
obj.__properties__[prop].value = obj[prop];
delete obj[prop];
(function (prop) {
Object.defineProperty(obj, prop, {
get: function () {
return obj.__properties__[prop].value;
},
set: function (newValue) {
var oldValue = obj.__properties__[prop].value;
if(oldValue !== newValue) {
var oldDepValues = {};
for(var dep in obj.__properties__[prop].dependents) {
if(obj.__properties__[prop].dependents.hasOwnProperty(dep)) {
oldDepValues[dep] = obj.__properties__[dep].value();
}
}
obj.__properties__[prop].value = newValue;
for(var i=0; i<obj.__properties__[prop].listeners.length; ++i) {
obj.__properties__[prop].listeners[i](oldValue, newValue);
}
for(dep in obj.__properties__[prop].dependents) {
if(obj.__properties__[prop].dependents.hasOwnProperty(dep)) {
var newDepValue = obj.__properties__[dep].value();
for(i=0; i<obj.__properties__[dep].listeners.length; ++i) {
obj.__properties__[dep].listeners[i](oldDepValues[dep], newDepValue);
}
}
}
}
}
});
})(prop);
}
}
return obj;
}
function listen(obj, prop, callback) {
if(!obj.__properties__) throw 'object is not observable';
obj.__properties__[prop].listeners.push(callback);
}
observable(person);
listen(person, 'fullName', function(oldValue, newValue) {
console.log('Name changed from "'+oldValue+'" to "'+newValue+'"');
});
person.lastName = 'Reynolds';
Which logs: 哪个日志:
Name changed from "Ryan Gosling" to "Ryan Reynolds"
名称从“ Ryan Gosling”更改为“ Ryan Reynolds”
The only problem I see is with defining methods such as fullName()
on the person object which would depend on the other two properties. 我看到的唯一问题是要在person对象上定义诸如
fullName()
方法,这将取决于其他两个属性。 This requires a little extra markup on the object to allow developers to specify the dependency. 这需要在对象上添加一些额外的标记,以允许开发人员指定依赖项。
Other than that, are there any downsides to this approach? 除此之外,这种方法还有什么缺点吗?
advent of getters and setters in JS 1.8.5 - are there any downsides to this approach?
JS 1.8.5中的getter和setter方法的问世-这种方法有什么缺点吗?
O(1)
) operation, while with getters/setters there might be a lot of more happening. O(1)
)操作,而使用getter / setter方法可能会发生更多的事情。 You will need to care not forgetting that, and the use of actual methods could help. So if we know what we are doing, yes, we can do better. 因此,如果我们知道自己在做什么,是的,我们可以做得更好。
Still, there is one huge point we need to remember: the synchronity/asynchronity (also have a look at this excellent answer ). 尽管如此,我们仍然需要记住一个重要的观点:同步/异步(也可以看看这个出色的答案 )。 Angular's dirty checking allows you to change a bunch of properties at once, before the event fires in the next event loop turn.
Angular的脏检查允许您在下一个事件循环回合触发事件之前立即更改一堆属性。 This helps to avoid (the propagation of) semantically invalid states.
这有助于避免语义无效状态(的传播)。
Yet I see the synchronous getters/setters as a chance as well. 然而,我也认为同步吸气剂/吸气剂也是一个机会。 They do allow us to declare the dependencies between properties and define the valid states by this.
它们确实允许我们声明属性之间的依赖关系,并由此定义有效状态。 It will automatically ensure the correctness of the model, while we only have to change one property at a time (instead of changing
firstName
and fullName
all the time, firstName
is enough). 它将自动确保模型的正确性,而我们一次只需要更改一个属性(而不是始终更改
firstName
和fullName
, firstName
就足够了)。 Nevertheless, during dependency resolving that might not hold true so we need to care about it. 但是,在依赖关系解析过程中,这可能并不成立,因此我们需要对此加以注意。
So, the listeners that are not related to the dependencies management should be fired asynchronous. 因此,与依赖项管理无关的侦听器应异步触发。 Just
setImmediate
their loop. 只需
setImmediate
他们的循环。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.