摘要:鏈式調(diào)用精髓在于重用一個初始操作可以把方法的鏈式調(diào)用技術(shù)寫到自己所寫的整個庫中把自己喜歡的方法串起來調(diào)用兩個部分一個創(chuàng)建代表元素的對象的工廠還有一批對這個元素執(zhí)行某些操作的方法每一個這種方法都可以在方法名前附加一個圓點后加入調(diào)用鏈中方法的鏈
鏈式調(diào)用 精髓在于重用一個初始操作.
addEvent($(".example"), "click", function () { // Without chaining: $(this).hide(); setStyle(this, "color", "green"); $(this).show(); // With chaining; $(this).hide().setStyle("color", "green").show(); });調(diào)用鏈結(jié)構(gòu)
$函數(shù)通常會返回一個HTML 元素(的集合):
function $() { var eles = []; for (var i = 0, len = arguments.length; i < len; ++i) { var ele = arguments[i]; if (typeof ele === "string") { ele = document.getElementById(ele); } if (arguments.length === 1) { return ele; } eles.push(ele); } return eles; }
如果把這個函數(shù)改造成一個構(gòu)造器,把那些元素作為數(shù)組保存在一個實例屬性中,并讓所有定義在構(gòu)造器函數(shù)的 prototype 屬性所指對象中的方法都返回用來調(diào)用方法的那個實例的引用,那么它就具有鏈式調(diào)用的能力.
做一下改進:首先把$函數(shù)改成一個工廠方法,負責(zé)創(chuàng)建支持鏈式調(diào)用的對象,這個函數(shù)應(yīng)該能接受元素數(shù)組形式的參數(shù),所以我們能夠使用和原來一樣的公有接口:
(function () { // Use a private class. function _$(els) { this.eles = []; for (var i = 0, len = els.length; i < len; ++i) { var ele = els[i]; if (typeod ele === "string") { ele = document.getElementById(ele); } this.eles.push(ele); } } // The public interface remains the same. window.$ = function () { return new _$(arguments); }; })();
由于所有對象都會繼承其原型對象的屬性和方法,所以我們可以讓定義在原型對象中的那幾個方法都返回用以調(diào)用方法的實例對象的引用,這樣就可以對哪些方法進行鏈式調(diào)用.現(xiàn)在在_$這個私有構(gòu)造函數(shù)的 prototype 對象中添加方法:
(function () { function _$(eles) { // ... } _$.prototype = { each: function (fn) { for (var i = 0, len = this.eles.length; i < len; ++i) { fn.call(this, this.eles[i]); } return this; }, hide: function (0 { var that = this; this.setStyle("display", "none"); }); setStyle: function (prop, val) { this.each(function (ele) { ele.style[prop] = val; }); return this; }, show: function (0 { var that = this; this.setStyle("display", "block"); }); return this; addEvent: function(type, fn) { var add = function (ele) { if (window.addEventListener) { ele.addEventList(type, fn, false); } else if (window.attachEvent) { ele.attachEvnet("on" + type, fn); } }; this.each(function (el) { add(el); }); return this; } }; window.$ = function () { return new _$(arguments); }; })();
每個方法的最后一行return this;會講調(diào)用方法對象傳給調(diào)用鏈上的下一個方法.
jQuery 便是這樣,window 對象或者某個 HTML 元素是調(diào)用鏈的錨點,多有操作都掛系在上面.
鏈式調(diào)用很適合于賦值器方法,但是對于取值器方法,并不希望方法返回 this.不過使用回調(diào)技術(shù)可以返回你想要的數(shù)據(jù)而不是 this.
// Accessor without function callbacks: returning requested data in accessors. window.API = window.API || function () { var name = "Hello world"; // Privilleged mutator this.setName = function(newName) { name = newName; return this; }; // Privileged accessor method. this.getName = function () { return name; }; }; // Implementation code var o = new API; console.log(o.getName()); // Displays "Hello world". console.log(o.setName("nanci").getName()); // Display "nanci" // Accessor with function callbacks. window.API2 = window.API2 || function () { var name = "Hello world"; // Privilleged mutator this.setName = function(newName) { name = newName; return this; }; // Privileged accessor method. this.getName = function (callback) { callback.call(this, name); return this; }; } // Implementation code var o2 = new API2; o2.getName(console.log).setName("nanci").getName(console.log); // Displays "Hello world" and then display "nanci"小結(jié)
使用鏈式調(diào)用可以避免多次重復(fù)使用一個對象變量,減少代碼量.
如果想讓類的接口保持一致,讓取值器像賦值器那樣也支持鏈式調(diào)用,那么可以使用回調(diào).
----------another part----------
工廠如果你想開幾個自行車商店,每個店都有幾種型號的自行車出售,用一個類表示:
// BicycleShop class. var BicycleShop = function () {}; BicycleShop.prototype = { sellBicycle: function(model) { var bicycle; switch(model) { case "The Speedter": bicycle = new Speedter(); break; case "The Lowrider": bicycle = new Lowrider(); break; case "The Comfort Cruiser": default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); bicycle.assemble(); bicycle.wash(); return bicycle; } };
sellBicycle 方法根據(jù)所要求的自行車型號用 switch 語句創(chuàng)建一個自行車的實例.各種型號的自行車實例可以互換使用,因為他們都實現(xiàn)了 Bicycle 接口(接口在工廠中很重要,如果不對對象進行某種類型檢查以其確保其實現(xiàn)了必須的方法,那么工廠模式并不能帶來什么好處).
// The Bicycle interface. var Bicycle = new Interface("Bicycle", ["assemble", "wash", "ride", "repair"]); // Speedster class. var Speedster = function () { // implement Bicycle ... }; Speedster.prototype = { assemble: function () { ... }, wash: function () { ... }, ride: function () { ... }, repair: function () { ... } };
要出售某種型號自行車,只需要調(diào)用 sellBicycle 方法即可:
var californiaCruisers = new BicycleShop(); var yourNewBike = californiaCruisers.sellBicycle("The Speedster");
如果你想在供貨目錄中加入一款新車型,更好的解決辦法是把 sellBicycle 方法中"創(chuàng)建新實例"這部分工作轉(zhuǎn)交給一個簡單工廠對象.
// BicycleFactory namespace. var BicycleFactory = { createBicycle: function (model) { var bicycle; switch(model) { case "The Speedter": bicycle = new Speedter(); break; case "The Lowrider": bicycle = new Lowrider(); break; case "The Comfort Cruiser": default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); return bicycle; } };
BicycleFactory 是一個單體,用來把 createBicycle 方法封裝在一個命名空間中,這個方法返回一個實現(xiàn)了 Bicycle接口的對象,然后可以對其進行組裝和清洗:
// BicycleShop class, improved. var BicycleShop = function () {}; BicycleShop.prototype = { sellBicycle: function (model) { var bicycle = BicycleFactory.createBicycle(model); bicycle.assemble(); bicycle.wash(); return bicycle; } };
這個 BicycleFactory 對象可以供各種類用來創(chuàng)建新的自行車實例.有關(guān)可供車型的所有信息都集中在一個地方管理,所以添加更多車型很容易:
// BicycleFactory namespace, with more models. var Bicycle: function (model) { var bicycle; switch (model) { case "The Speedster": bicycle = new Speedster(); break; case "The Lowrider": bicycle = new Lowrider(); break; case "The Flatlander": bicycle = new Flatlander(); break; case "The ComfortCruiser": bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); return bicycle; }
這是一個簡單工廠的例子,他把成員對象的創(chuàng)建工作交給一個外部對象,這個外部對象可以是一個簡單的命名空間,也可以是一個類的實例.
示例: XHR 工廠用 Ajax 技術(shù)發(fā)起異步請求是現(xiàn)在 Web 開發(fā)的一個常見任務(wù).用于發(fā)起請求的對象是某種類的實例,具體是哪種類取決于用戶的瀏覽器.如果代碼中需要多次執(zhí)行 ajax 請求,那么可以把創(chuàng)建這種對象的代碼提取到一個類中,并創(chuàng)建一個包裝器來包裝在實際發(fā)起請求時所要經(jīng)歷的一系列步驟,簡單工廠非常適合該場合,根據(jù)瀏覽器特性生成一個 XMLHttpRequest 或者 ActiveXObject 實例.
// AjaxHandler interface. var AjaxHandler = new Interface("AjaxHandler", ["request", "createXhrObject"]); // SimpleHandler class. var SimleHandler = function () {}; // implements AjaxHandler SimpleHandler.prototype = { request: function (method, url, callback, postVars) { var xhr = this.createXhrObject(); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; (xhr.status === 200) ? callback.success(xhr.responseText) : callback.failure(xhr.status); }; xhr.open(method, url, true); if (method !== "POST") { postVars = null; } xhr.send(postVars); }, createXhrObject: function () { // Factory method. var methods = [ function () { return newXMLHttpRequest(); }, function () { return new ActiveXObject("Msxml2.XMLHTTP"); }, function () { return new ActiveXObject("Microsoft.XMLHTTP"); } ]; for (var i =0, len = methods.length; i < len; i++) { try { methods[i](); } catch(e) { continue; } //If we reach this point, method[i] worked. this.createXhrObject = methods[i]; //Memoize the method. return methods[i]; } // If we reach this point, none of the methods worked. throw new Error("SimpleHandler: Could not create an XHR object."); } }利
主要好處在于消除對象間的耦合,通過使用工廠方法而不是 new 關(guān)鍵字及具體類,你可以把所有實例化代碼集中在一個位置.可以大大簡化更換所用的類或者在運行期間動態(tài)選擇所有的類的工作.在派生子類時也更靈活.可以先創(chuàng)建一個抽象的超類,然后在子類中創(chuàng)建工廠方法,從而把成員對象的實例化推遲到更專門化的子類中進行.
所有這些好處都和面向?qū)ο笤O(shè)計的兩條原則相關(guān): 弱化對象間的耦合:防止代碼的重復(fù).在一個方法中進行類的實例化,可以消除重復(fù)性的代碼.這是在用一個對接口的調(diào)用取代一個具體的實現(xiàn).這些都有助于模塊化代碼.
不能把工廠方法當(dāng)萬金油,而把普通函數(shù)扔在以便.如果根本不可能另外換用一個類或者不需要在運行期間在一系列類的選擇,那么就不應(yīng)該使用工廠方法.大多數(shù)最好使用 new 關(guān)鍵字和構(gòu)造函數(shù)公開進行實例化,這樣代碼會更簡單易讀..一眼就看到調(diào)用的構(gòu)造函數(shù),不必去查看某個工廠方法去知道實例化的是什么類.
小結(jié)文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87903.html
之前也有和大家講過有關(guān)JS的對象創(chuàng)建和對象繼承,本篇文章主要為大家做個匯總和梳理?! S中其實就是原型鏈繼承和構(gòu)造函數(shù)繼承的毛病,還有就是工廠、構(gòu)造、原型設(shè)計模式與JS繼承。 JS高級程序設(shè)計4:class繼承的重點,不只是簡簡單單的語法而已?! ο髣?chuàng)建 不難發(fā)現(xiàn),每一篇都離不開工廠、構(gòu)造、原型這3種設(shè)計模式中的至少其一! 那JS為什么非要用到這種3種設(shè)計模式了呢?? 我們先從對...
原文中詳細的講解了Pytest架構(gòu)之fixture,原文中根據(jù)實例編碼推薦的十分詳盡。對大家學(xué)習(xí)培訓(xùn)和工作具有很強的參閱參考意義,需用的小伙伴可以參考一下 原文中有關(guān)fixture的具體內(nèi)容如下所示: 1、參數(shù)化設(shè)計fixture 2、fixture工廠 3、request這一fixture 1、參數(shù)化設(shè)計fixture fixture有個params主要參數(shù),容許大家傳送數(shù)據(jù)?!?..
摘要:第二個調(diào)用當(dāng)前執(zhí)行的函數(shù),并為其設(shè)置另外一個定時器。使得在前一個定時器代碼執(zhí)行完之前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔。 在自己用canvas畫一個時鐘時,畫秒鐘用的是利用圖片將重復(fù)的線條遮住,但是會出現(xiàn)有兩個秒鐘線條同時存在,才想起setInterval有那么個坑,查了點資料,記錄下,若有不對的或者未寫到的點,還請大家指出,謝謝^_^ 在此之前先科普下這個學(xué)習(xí)點...
閱讀 1856·2021-11-24 09:39
閱讀 2318·2021-09-30 09:47
閱讀 4208·2021-09-22 15:57
閱讀 1918·2019-08-29 18:36
閱讀 3609·2019-08-29 12:21
閱讀 621·2019-08-29 12:17
閱讀 1294·2019-08-29 11:25
閱讀 755·2019-08-28 18:26