摘要:更細致的為了應對更為復雜的對象合并要求,提供了方法。注意到,當兩者都為時,得到的正是在配置對象合并中的一個理想結果。它們都類似于對象合并,而且有趣的是,其源碼中都調用了方法。其深合并的實現方法依然是遞歸。
起因:配置選項
“配置”(Config | Options | Settings)對大家來說一定是非常熟悉的詞。就以一般玩的游戲為例,很多游戲的主界面,搭配的菜單會是"start","load","config","exit"這樣的搭配。在"config"中,我們可以調整游戲參數,比如音量、控制按鍵等,以更符合自己的游戲習慣。不過,也有游戲并沒有"config",這些游戲其實就是只有一個不允許修改的“默認”(Defaults)配置,而即使如此,這些游戲至少也會給你一份指南,告訴你應該如何按照預設進行游戲。
應用的配置這一環節的設計,其實就是要求要將配置數據從代碼中分離出來。也就是說,那些可以被修改,或者將來可能會被修改的內容(這些內容也是代碼),應該和指令類的代碼區別開來,多帶帶放在一個地方。這樣,源碼修改可以減少很多意外的錯誤。
javascript編寫的封裝的功能模塊,比如常見的jQuery插件,一般都會這樣分離出配置數據。下面是[slidesjs][]的使用范例:
$("#slides").slidesjs({ width: 940, height: 528 });
這里的{width: 940, height: 528}即是配置數據。不過,只是這些數據是不能獨立支撐起完整的功能的。在封裝的代碼之內的,還隱藏著完整的初始配置數據,這就是默認配置。而像上面這樣在實際使用時傳入的配置數據,一般叫做用戶配置。它們之間的關系是,如果同時包含對某一選項的配置值,用戶配置會覆蓋掉默認配置。
比如在Sublime Text中,就是這種設計(Default和User):
可以想到,實際功能運行時,使用的配置數據既不是默認配置,也不是用戶配置,而是這兩個配置的結合。在javascript中,配置數據都是對象(Object)的形式,因此,我們需要做對象合并。
在YUI的核心包(yui-core)中,就提供了對象合并有關的方法。
YUI中的對象合并方法 淺合并的Y.merge()YUI提供了合并一個或更多對象到一個新的合并對象上的方法Y.merge(),在這里先使用它。假如有一個封裝的功能模塊(簡單的游戲?),然后內部通過Y.merge()進行配置對象的合并:
var myModule = function(userConfig){ var defaults = { volume: 0, quality: "normal", control: { left: "left", right: "right", attack: "space" } }; var config = Y.merge(defaults, userConfig); // use the merged config to run this module... };
對應上面已有的默認配置的形式,給出如下的用戶配置:
var userConfig = { quality: "high", control: { left: "a", right: "d", extra: "z" } };
這里通過Y.merge()合并得到的config,輸出結果是:
{ volume: 0, quality: "high", control: { left: "a", right: "d", extra: "z" } }
可以看到,volume和quality的屬性合并都達到了預想的要求。但是,屬性control卻像是忽略了默認值的部分,并不符合預想結果。這是因為,屬性control的值并不是基本數據類型,而是引用數據類型(例如對象、數組或函數),因此這里的配置對象是包含多層次的復雜配置對象。Y.merge()并沒有處理這樣的情況,它的源碼如下:
Y.merge = function () { var i = 0, len = arguments.length, result = {}, key, obj; for (; i < len; ++i) { obj = arguments[i]; for (key in obj) { if (hasOwn.call(obj, key)) { result[key] = obj[key]; } } } return result; };
其中hasOwn是Object.prototype.hasOwnProperty。從上面的源碼可以看出,Y.merge()只對直接屬性(層級數為1)進行賦值,并沒有分析屬性的值類別。因此,在前面的對象合并中,config的control屬性,實際上就和userConfig的control是同一個引用,如果在config上修改control對象,則也會改變userConfig的control對象。
不過,Y.merge()仍然是很有價值的對象合并方法。如果是單層次的配置對象,它足以實現預想的要求,這是它很有用的一個模式。此外,請注意這個方法創建了一個空對象result用作最終返回,而不是直接對參數進行操作,所以,Y.merge()方法不會影響作為參數的任一對象。
更細致的Y.mix()為了應對更為復雜的對象合并要求,YUI提供了方法Y.mix()。Y.mix()不能像Y.merge()那樣同時合并2個以上的對象,它的合并對象數目限定為2個,對應前2個參數,此外的參數都用作合并模式、方法的設置。Y.mix()搭配其多種模式和配置的控制,可以實現相當精細的兩個對象的合并(方法叫做"mix",所以也稱為混合吧)。
Y.mix()的源碼較多,不直接貼在本文中。YUI官方的注釋寫得很詳細,不過在這里我也說一些我自己的理解。它的源碼形式是:
Y.mix = function(receiver, supplier, overwrite, whitelist, mode, merge) { // Y.mix() detail );
Y.mix()有很多參數。前2個參數receiver和supplier如字面意思,分別對應合并操作的接收者和提供者。其余4個參數的意義分別是:
overwrite,默認為false。如果為true則會開啟屬性覆蓋。也就是說,默認情況下,提供者的屬性是不會覆蓋接受者的同名屬性的。
whitelist,白名單。可以指定一個數組,只有當提供者的屬性名包含于白名單數組內時,屬性才會被拷貝到接受者。
mode,混合模式。可選值0、1、2、3、4分別對應5個模式。默認值為0,是object to object的合并方式。
merge,默認為false。如果為true,對象上的值為引用數據類型的屬性(也就是,當這是一個多層次的復雜對象時),每一個層級上的對象都會依次被合并,而不是被忽略(overwrite為false)或被直接覆蓋(overwrite為true)。
上面的參數中,overwrite和merge的意義比較重要,其他的則一般使用null或默認值。為了理解overwrite和merge,直接進行不同搭配的合并測試(配置變量和前文一樣)。測試代碼和結果如下:
// overwrite = false , merge = false | Y.mix()的最簡單形式,默認值 var config = Y.mix(defaults, userConfig); // 結果輸出: // {volume => 0, quality => normal, control => {left => left, right => right, attack => space}} // overwrite = true , merge = false var config = Y.mix(defaults, userConfig, true, null, 0, false); // 結果輸出: // {volume => 0, quality => high, control => {left => a, right => d, extra => e}} // overwrite = false , merge = true var config = Y.mix(defaults, userConfig, false, null, 0, true); // 結果輸出: // {volume => 0, quality => normal, control => {left => left, right => right, attack => space, extra => e}} // overwrite = true , merge = true var config = Y.mix(defaults, userConfig, true, null, 0, true); // 結果輸出: // {volume => 0, quality => high, control => {left => a, right => d, attack => space, extra => e}}
可以看出,4個結果各不相同。從這些結果的具體情況,可以分析體會overwrite和merge的意義。注意到,當兩者都為true時,得到的正是在配置對象合并中的一個理想結果。而當overwrite = true, merge = false時,得到的結果和Y.merge()相同。
閱讀Y.mix()的源碼,可以了解到,merge決定是否進行深合并。深合并的實現原理是遞歸,也就是對那些需要多層次合并的屬性值(對象、數組),再次調用Y.mix()。overwrite決定是否進行覆蓋,這就類似于電腦里做復制操作時,會提示同名文件是否覆蓋的選項。
對于用作配置數據的對象而言,預期的最佳的合并方式就是允許覆蓋,而且進行深合并。因此,Y.mix(defaults, userConfig, true, null, 0, true)即可以得到最適當的合并后的配置數據。
需要注意的是,Y.mix()直接對接收者進行操作,并返回接收者。所以,和Y.merge()不同,它一定會影響到作為第一個參數的對象。
如果可以確定配置數據是單層次的簡單配置對象,那么使用Y.merge()要更簡單方便。
更多的工具YUI除了核心包內的上面兩個方法外,還在oop模塊中提供了一些處理對象的附加工具。其中包括了Y.extend()(單詞意義:擴展),Y.augment()(單詞意義:增強),Y.aggregate()(單詞意義:聚合)。它們都類似于對象合并,而且有趣的是,其源碼中都調用了Y.mix()方法。
這幾個方法和前面的有哪些不同呢?本文限于篇幅,只簡單說一些。Y.aggregate()是開啟深合并的Y.mix()的一個方法糖,Y.extend()則要求參數是構造函數,它操作的是傳入對象的prototype,并通過prototype建立繼承關系(原型鏈),Y.augment()是從提供者拷貝方法到接收者,但它巧妙地隔離了所有拷貝的方法并延遲調用提供者的構造函數,直到你第一次調用被拷貝上去的新方法。
有些關系的jQuery.extend()因為比較近似,所以在這里也提一下jQuery。
如果你寫過jQuery插件,你肯定知道jQuery.extend()這個方法。事實證明,不同的javascript庫,在命名理念上也是各有想法,在此請注意,jQuery.extend()和Y.extend()是完全不同的兩個方法。閱讀jQuery.extend()的源碼可知,它仍然是在做傳統的對象合并,而且同時允許聲明一個可選的邏輯參數deep,來指定是否進行深合并。其深合并的實現方法依然是遞歸。
此外,類似于Y.mix(),jQuery.extend()也會影響到作為接收者的對象。官方文檔對此還給了明確說明,如果希望不影響原來的對象,就使用var object = $.extend({}, object1, object2);這樣以空對象作為接收者的寫法。
結語之所以想到寫這個話題,大概就是因為YUI里有關對象合并很有幾個不同的方法,我已經相當被它們弄暈了。所以,果斷讀源碼,才能理清這其中的區別,也算是體會一下javascript庫設立這些不同的方法的本因吧。
默認配置 + 用戶配置 → 實際配置,結果來看也是一個不簡單的轉化計算式。
(重新編輯自我的博客,原文地址:http://acgtofe.com/posts/2014/07/object-toolkit-in-yui)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87600.html
摘要:所有依賴這個模塊的語句,都定義在一個回調函數中,等到所有依賴加載完成之后前置依賴,這個回調函數才會運行。如果將前面的代碼改寫成形式,就是下面這樣定義了一個文件,該文件依賴模塊,當模塊加載完畢之后執行回調函數,這里并沒有暴露任何變量。 模塊化是我們日常開發都要用到的基本技能,使用簡單且方便,但是很少人能說出來但是的原因及發展過程。現在通過對比不同時期的js的發展,將JavaScript模...
摘要:參考精讀模塊化發展模塊化七日談前端模塊化開發那點歷史本文先發布于我的個人博客模塊化開發的演進歷程,后續如有更新,可以查看原文。 Brendan Eich用了10天就創造了JavaScript,因為當時的需求定位,導致了在設計之初,在語言層就不包含很多高級語言的特性,其中就包括模塊這個特性,但是經過了這么多年的發展,如今對JavaScript的需求已經遠遠超出了Brendan Eich的...
摘要:執行環境在很多方面都有其獨特之處全局變量和函數便是其中之一事實上的初始執行環境是由多種多樣的全局變量所定義的這寫全局變量在腳本環境創建之初就已經存在了我們說這些都是掛載在全局對象上的全局對象是一個神秘的對象它表示了腳本最外層上下文在瀏覽器中 JavaScript執行環境在很多方面都有其獨特之處. 全局變量和函數便是其中之一. 事實上, js的初始執行環境是由多種多樣的全局變量所定義的,...
摘要:最近在全力整理高性能的文檔,并重新學習一遍,放在這里方便大家查看并找到自己需要的知識點。 最近在全力整理《高性能JavaScript》的文檔,并重新學習一遍,放在這里方便大家查看并找到自己需要的知識點。 前端開發文檔 高性能JavaScript 第1章:加載和執行 腳本位置 阻止腳本 無阻塞的腳本 延遲的腳本 動態腳本元素 XMLHTTPRequest腳本注入 推薦的無阻塞模式...
摘要:我是這一期的主持人黃子毅本期精讀的文章是。模塊化需要保證全局變量盡量干凈,目前為止的模塊化方案都沒有很好的做到這一點。精讀本次提出獨到觀點的同學有流形,黃子毅,蘇里約,,楊森,淡蒼,留影,精讀由此歸納。 這次是前端精讀期刊與大家第一次正式碰面,我們每周會精讀并分析若干篇精品好文,試圖討論出結論性觀點。沒錯,我們試圖通過觀點的碰撞,爭做無主觀精品好文的意見領袖。 我是這一期的主持人 ——...
閱讀 3318·2023-04-25 16:25
閱讀 3823·2021-11-15 18:01
閱讀 1600·2021-09-10 11:21
閱讀 3007·2021-08-02 16:53
閱讀 3081·2019-08-30 15:55
閱讀 2489·2019-08-29 16:24
閱讀 2098·2019-08-29 13:14
閱讀 1027·2019-08-29 13:00