私は最近、Angular や Meteor などの JavaScript フレームワークを調べていて、DOM を更新できるようにオブジェクト プロパティがいつ変更されたかをどのように認識しているのか疑問に思っていました。
Angular がフックして必要な更新を実行できるように、ある種のゲッター/セッターを呼び出す必要がなく、単純な古い JS オブジェクトを使用していたことに少し驚きました。私の理解では、変更のためにオブジェクトを定期的にポーリングするだけです。
しかし、 JS 1.8.5でのゲッターとセッターの出現により、それよりも優れたことができるようになりましたね。
ちょっとした概念実証として、次のスクリプトをまとめました。
(編集:依存プロパティ/メソッドのサポートを追加するためにコードを更新)
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';
どのログ:
名前を「ライアン・ゴズリング」から「ライアン・レイノルズ」に変更
fullName()
私が目にする唯一の問題は、他の 2 つのプロパティに依存する人物オブジェクトなどのメソッドを定義することです。これには、開発者が依存関係を指定できるように、オブジェクトに少し追加のマークアップが必要です。
それ以外に、このアプローチの欠点はありますか?