10

私は最近、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 つのプロパティに依存する人物オブジェクトなどのメソッドを定義することです。これには、開発者が依存関係を指定できるように、オブジェクトに少し追加のマークアップが必要です。

それ以外に、このアプローチの欠点はありますか?

Jsフィドル

4

1 に答える 1

1

JS 1.8.5 でのゲッターとセッターの出現 - このアプローチには欠点がありますか?

  • 観察されたもの以外のプロパティの変更はキャプチャしません。確かに、これはモデル化されたエンティティ オブジェクトには十分であり、プロキシを使用できる他のものには十分です。
  • ゲッター/セッター、さらにはプロキシをサポートするブラウザーに限定されます。しかし、時代遅れのブラウザーを気にする人はいますか? :-) 制限された環境 (Node.js) では、これはまったく当てはまりません。
  • アクセサー プロパティ (getter と setter を使用) は、実際の get/set メソッドよりもはるかに低速です。もちろん、それらが重要なセクションで使用されるとは思っていません。また、コードをより洗練されたものにすることができます。それでも、それを心の奥底に留めておく必要があります。また、派手なコードは誤解を招く可能性があります。通常、プロパティの割り当て/アクセスは短い ( O(1)) 操作であると予想されますが、getter/setter ではさらに多くのことが起こる可能性があります。それを忘れないように注意する必要があり、実際のメソッドを使用すると役立つ場合があります。

ですから、私たちが何をしているのかを知っていれば、そうです、より良くすることができます.

それでも、覚えておく必要がある大きなポイントが 1 つあります。それは、同期性/非同期性です (この優れた回答もご覧ください)。Angular のダーティ チェックにより、次のイベント ループ ターンでイベントが発生する前に、一連のプロパティを一度に変更できます。これは、意味的に無効な状態 (の伝搬) を回避するのに役立ちます。

それでも、同期ゲッター/セッターもチャンスだと思います。プロパティ間の依存関係を宣言し、これによって有効な状態を定義することができます。モデルの正確性が自動的に保証されますが、一度に 1 つのプロパティを変更するだけで済みます (常に変更するのfirstNameではなく、十分です)。それにもかかわらず、依存関係の解決中には当てはまらない可能性があるため、注意する必要があります。fullNamefirstName

したがって、依存関係の管理に関係のないリスナーは非同期で起動する必要があります。setImmediate彼らのループだけです。

于 2013-02-10T20:24:06.757 に答える