簡體   English   中英

使對象可見

[英]Making objects observable

最近,我一直在研究Angular和Meteor等JavaScript框架,我想知道他們如何知道何時更改了對象屬性,以便可以更新DOM。

Angular使用普通的舊JS對象而不是要求您調用某種getter / setter,以便它可以掛接並進行必要的更新,這讓我感到有些驚訝。 我的理解是,它們只是定期輪詢對象以進行更改。

但是隨着JS 1.8.5中getter和setter方法的出現,我們可以做得更好,不是嗎?

作為一點概念驗證,我整理了以下腳本:

編輯:更新代碼以添加從屬屬性/方法支持)

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';

哪個日志:

名稱從“ Ryan Gosling”更改為“ Ryan Reynolds”

我看到的唯一問題是要在person對象上定義諸如fullName()方法,這將取決於其他兩個屬性。 這需要在對象上添加一些額外的標記,以允許開發人員指定依賴項。

除此之外,這種方法還有什么缺點嗎?

JsFiddle

JS 1.8.5中的getter和setter方法的問世-這種方法有什么缺點嗎?

  • 除了觀察到的以外,您不會捕獲任何屬性更改。 當然,對於建模實體對象以及我們可以使用代理的任何其他對象,這已經足夠。
  • 它僅限於支持getter / setter的瀏覽器,甚至支持代理。 但是,誰在乎過時的瀏覽器? :-)在受限環境(Node.js)中,這根本不成立。
  • 訪問器屬性(帶有getter和setter)比實際的get / set方法要慢得多。 當然,我不希望它們在關鍵部分中使用,它們會使代碼看起來更奇特。 但是,您需要牢記這一點。 同樣,看似花哨的代碼可能會導致誤解-通常,您希望屬性分配/訪問是一個簡短的( O(1) )操作,而使用getter / setter方法可能會發生更多的事情。 您需要注意不要忘記這一點,使用實際方法可能會有所幫助。

因此,如果我們知道自己在做什么,是的,我們可以做得更好。

盡管如此,我們仍然需要記住一個重要的觀點:同步/異步(也可以看看這個出色的答案 )。 Angular的臟檢查允許您在下一個事件循環回合觸發事件之前立即更改一堆屬性。 這有助於避免語義無效狀態(的傳播)。

然而,我也認為同步吸氣劑/吸氣劑也是一個機會。 它們確實允許我們聲明屬性之間的依賴關系,並由此定義有效狀態。 它將自動確保模型的正確性,而我們一次只需要更改一個屬性(而不是始終更改firstNamefullNamefirstName就足夠了)。 但是,在依賴關系解析過程中,這可能並不成立,因此我們需要對此加以注意。

因此,與依賴項管理無關的偵聽器應異步觸發。 只需setImmediate他們的循環。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM