摘要:構造函數模式是中最常用的模式之一,用于創建給定類型的新對象。原型模式這是基于對象的創造型設計模式。它通常用于目標對象受到約束且可能無法有效地處理其所有職責的情況。它不是構造函數其靜態方法用于可攔截的操作。
原文:https://medium.com/better-programming...簡介
譯者:前端技術小哥
我們編寫代碼是為了解決問題。 這些問題通常有很多相似之處,在嘗試解決這些問題時,我們會注意到幾種常見的模式。 這就是設計模式的用武之地。
設計模式這個術語在軟件工程中用來表示軟件設計中經常出現的問題的通用的、可重用的解決方案。
設計模式的基本概念從一開始就存在于軟件工程行業,但它們并沒有真正正式化。由Erich Gamma、Richard Helm、Ralph Johnson和John Vlisides,著名的四人幫(GoF),編寫的《設計模式:可復用面對對象軟件的基礎》一書有助于推動軟件工程中設計模式的形式化概念。現在,設計模式是軟件開發的重要組成部分,并且已經存在很長時間了。
原著介紹了23種設計模式。
設計模式的好處有很多。它們是業界資深人士已經嘗試和測試過的經過驗證的解決方案。它們是可靠的方法,能夠以廣泛接受的方式解決問題,并反映出幫助定義問題的行業領先開發人員的經驗和見解。模式還使我們的代碼更具可重用性和可讀性,同時大大加快了開發過程。
設計模式絕不是最終的解決方案。它們只是為我們提供了解決問題的方法或方案。
注意:在本文中,我們將主要從面向對象的角度以及它們在現代JavaScript中的可用性來討論設計模式。這就是為什么許多來自GoF的經典模式不會被提及,而來自AddyOsmani的《JavaScript設計模式》中的一些現代模式將被囊括。為了便于理解,這些示例都保持簡單,因此不是各自設計模式的最佳實現。
設計模式通常分為三大類。
創建型模式顧名思義,這些模式用于處理對象創建機制。創造性設計模式基本上通過控制對象的創建過程來解決問題。
我們將詳細討論以下模式: 建造者模式、工廠模式、原型模式和單例模式。
這些模式與類和對象組合有關。它們幫助構造或重組一個或多個部分,而不影響整個系統。換句話說,它們幫助獲得新的功能,而不改變現有的功能。
我們將詳細討論以下模式:適配器模式、復合模式、裝飾模式、外觀模式、享元模式和代理模式。
這些模式涉及改善不同對象之間的通信。
我們將詳細討論以下模式:責任鏈模式、命令模式、迭代器模式、中介者模式、觀察者模式、狀態模式、策略模式和模板方法模式。
這是一個基于類的創造性設計模式。構造函數是一種特殊的函數,可用于用該函數定義的方法和屬性實例化新對象。
它不是經典的設計模式之一。事實上,在大多數面向對象的語言中,它更像是一種基本的語言構造,而不是模式。但是在JavaScript中,對象可以在沒有任何構造函數或“類”定義的情況下動態創建。因此,我認為以這個簡單模式為其他模式打下基礎是很重要的。
構造函數模式是JavaScript中最常用的模式之一,用于創建給定類型的新對象。
在下面的例子中,我們定義了一個類Hero,它具有name和specialAbility等屬性,以及getDetails等方法。然后,我們通過調用構造函數方法實例化一個對象IronMan,該方法使用new關鍵字作為參數傳入相應屬性的值。
// traditional Function-based syntax function Hero(name, specialAbility) { // setting property values this.name = name; this.specialAbility = specialAbility; // declaring a method on the object this.getDetails = function() { return this.name + " can " + this.specialAbility; }; } // ES6 Class syntax class Hero { constructor(name, specialAbility) { // setting property values this._name = name; this._specialAbility = specialAbility; // declaring a method on the object this.getDetails = function() { return `${this._name} can ${this._specialAbility}`; }; } } // creating new instances of Hero const IronMan = new Hero("Iron Man", "fly"); console.log(IronMan.getDetails()); // Iron Man can fly工廠模式
工廠模式是另一種基于類的創造模式。在這里,我們提供了一個泛型接口,它將對象實例化的責任委托給它的子類。
當我們需要管理或操作不同但具有許多相似特征的對象集合時,經常使用這種模式。
在下面的例子中,我們創建了一個名為BallFactory的工廠類,它有一個接受參數的方法,根據參數,它將對象實例化職責委托給相應的類。如果類型參數是"football"或"soccer"對象實例化由Football類處理,但是如果類型參數是"basketball"對象實例化則由Basketball類處理。
class BallFactory { constructor() { this.createBall = function(type) { let ball; if (type === "football" || type === "soccer") ball = new Football(); else if (type === "basketball") ball = new Basketball(); ball.roll = function() { return `The ${this._type} is rolling.`; }; return ball; }; } } class Football { constructor() { this._type = "football"; this.kick = function() { return "You kicked the football."; }; } } class Basketball { constructor() { this._type = "basketball"; this.bounce = function() { return "You bounced the basketball."; }; } } // creating objects const factory = new BallFactory(); const myFootball = factory.createBall("football"); const myBasketball = factory.createBall("basketball"); console.log(myFootball.roll()); // The football is rolling. console.log(myBasketball.roll()); // The basketball is rolling. console.log(myFootball.kick()); // You kicked the football. console.log(myBasketball.bounce()); // You bounced the basketball.原型模式
這是基于對象的創造型設計模式。在這里,我們使用現有對象的某種“骨架”來創建或實例化新對象。
這種模式對JavaScript特別重要和有益,因為它使用原型繼承而不是經典的面向對象繼承。因此,它發揮了JavaScript的優勢,并具有本機支持。
在下面的例子中,我們使用一個對象car作為原型,用JavaScript的Object.create特點創建另一個對象myCar。并在新對象上定義一個額外的屬性owner。
// using Object.create as was recommended by ES5 standard const car = { noOfWheels: 4, start() { return "started"; }, stop() { return "stopped"; }, }; // Object.create(proto[, propertiesObject]) const myCar = Object.create(car, { owner: { value: "John" } }); console.log(myCar.__proto__ === car); // true適配器模式
這是一種結構型模式,其中一個類的接口被轉換成另一個類。這種模式允許由于接口不兼容而無法協同工作的類一起工作。
此模式通常用于為新的重構API創建包裝器,以便其他現有舊API仍然可以使用它們。這通常是在新的實現或代碼重構(出于性能提高等原因)導致不同的公共API時完成的,而系統的其他部分仍在使用舊的API,需要進行調整才能協同工作。
在下面的例子中,我們有一個舊的API,即類OldCalculator,和一個新的API,即類NewCalculator。類OldCalculator為加法和減法提供了一個operation方法,而NewCalculator為加法和減法提供了多帶帶的方法。適配器類CalcAdapter將NewCalculator包裹來將操作方法添加到面向公共的API中,同時在底層使用自己的加減法。
// old interface class OldCalculator { constructor() { this.operations = function(term1, term2, operation) { switch (operation) { case "add": return term1 + term2; case "sub": return term1 - term2; default: return NaN; } }; } } // new interface class NewCalculator { constructor() { this.add = function(term1, term2) { return term1 + term2; }; this.sub = function(term1, term2) { return term1 - term2; }; } } // Adapter Class class CalcAdapter { constructor() { const newCalc = new NewCalculator(); this.operations = function(term1, term2, operation) { switch (operation) { case "add": // using the new implementation under the hood return newCalc.add(term1, term2); case "sub": return newCalc.sub(term1, term2); default: return NaN; } }; } } // usage const oldCalc = new OldCalculator(); console.log(oldCalc.operations(10, 5, "add")); // 15 const newCalc = new NewCalculator(); console.log(newCalc.add(10, 5)); // 15 const adaptedCalc = new CalcAdapter(); console.log(adaptedCalc.operations(10, 5, "add")); // 15;復合模式
這是一種結構型設計模式,它將對象組合成樹狀結構來表示整個部分的層次結構。在此模式中,類樹結構中的每個節點可以是單個對象,也可以是對象的組合集合。無論如何,每個節點都被統一處理。
將這種模式可視化是有點復雜的。思考這個問題最簡單的方法是使用多層次菜單的例子。每個節點可以是一個不同的選項,也可以是菜單本身,它的子節點有多個選項。帶有子組件的節點組件是復合組件,而沒有子組件的節點組件是葉子組件。
在下面的例子中,我們創建了一個Component基類,它實現了所需的公共功能,并抽象了所需的其他方法。基類還有一個靜態方法,它使用遞歸遍歷由子類構成的復合樹結構。然后,我們創建兩個子類來擴展基類—不包含任何子類的Leaf和可以包含子類的Composite—因此具有處理添加、搜索和刪除子功能的方法。這兩個子類用于創建復合結構—在本例中是樹。
class Component { constructor(name) { this._name = name; } getNodeName() { return this._name; } // abstract methods that need to be overridden getType() {} addChild(component) {} removeChildByName(componentName) {} removeChildByIndex(index) {} getChildByName(componentName) {} getChildByIndex(index) {} noOfChildren() {} static logTreeStructure(root) { let treeStructure = ""; function traverse(node, indent = 0) { treeStructure += `${"--".repeat(indent)}${node.getNodeName()} `; indent++; for (let i = 0, length = node.noOfChildren(); i < length; i++) { traverse(node.getChildByIndex(i), indent); } } traverse(root); return treeStructure; } } class Leaf extends Component { constructor(name) { super(name); this._type = "Leaf Node"; } getType() { return this._type; } noOfChildren() { return 0; } } class Composite extends Component { constructor(name) { super(name); this._type = "Composite Node"; this._children = []; } getType() { return this._type; } addChild(component) { this._children = [...this._children, component]; } removeChildByName(componentName) { this._children = [...this._children].filter(component => component.getNodeName() !== componentName); } removeChildByIndex(index) { this._children = [...this._children.slice(0, index), ...this._children.slice(index + 1)]; } getChildByName(componentName) { return this._children.find(component => component.name === componentName); } getChildByIndex(index) { return this._children[index]; } noOfChildren() { return this._children.length; } } // usage const tree = new Composite("root"); tree.addChild(new Leaf("left")); const right = new Composite("right"); tree.addChild(right); right.addChild(new Leaf("right-left")); const rightMid = new Composite("right-middle"); right.addChild(rightMid); right.addChild(new Leaf("right-right")); rightMid.addChild(new Leaf("left-end")); rightMid.addChild(new Leaf("right-end")); // log console.log(Component.logTreeStructure(tree)); /* root --left --right ----right-left ----right-middle ------left-end ------right-end ----right-right */裝飾器模式
class Book { constructor(title, author, price) { this._title = title; this._author = author; this.price = price; } getDetails() { return `${this._title} by ${this._author}`; } } // decorator 1 function giftWrap(book) { book.isGiftWrapped = true; book.unwrap = function() { return `Unwrapped ${book.getDetails()}`; }; return book; } // decorator 2 function hardbindBook(book) { book.isHardbound = true; book.price += 5; return book; } // usage const alchemist = giftWrap(new Book("The Alchemist", "Paulo Coelho", 10)); console.log(alchemist.isGiftWrapped); // true console.log(alchemist.unwrap()); // "Unwrapped The Alchemist by Paulo Coelho" const inferno = hardbindBook(new Book("Inferno", "Dan Brown", 15)); console.log(inferno.isHardbound); // true console.log(inferno.price); // 20外觀模式
這是一種在JavaScript庫中廣泛使用的結構設計模式。它用于提供一個統一的、更簡單的、面向公眾的接口,以便于使用,從而避免了其組成子系統或子類的復雜性。
這種模式的使用在jQuery這樣的庫中非常常見。
在這個例子中,我們創建了一個面向公共的API,其中包含了一個類ComplaintRegistry。它只公開客戶端要使用的一種方法,即registerComplaint。它根據類型參數內部處理ProductComplaint或ServiceComplaint所需對象的實例化。它還處理所有其他復雜的功能,如生成唯一的ID、將complaints存儲在內存中等等。但是,使用外觀模式隱藏了所有這些復雜性。
let currentId = 0; class ComplaintRegistry { registerComplaint(customer, type, details) { const id = ComplaintRegistry._uniqueIdGenerator(); let registry; if (type === "service") { registry = new ServiceComplaints(); } else { registry = new ProductComplaints(); } return registry.addComplaint({ id, customer, details }); } static _uniqueIdGenerator() { return ++currentId; } } class Complaints { constructor() { this.complaints = []; } addComplaint(complaint) { this.complaints.push(complaint); return this.replyMessage(complaint); } getComplaint(id) { return this.complaints.find(complaint => complaint.id === id); } replyMessage(complaint) {} } class ProductComplaints extends Complaints { constructor() { super(); if (ProductComplaints.exists) { return ProductComplaints.instance; } ProductComplaints.instance = this; ProductComplaints.exists = true; return this; } replyMessage({ id, customer, details }) { return `Complaint No. ${id} reported by ${customer} regarding ${details} have been filed with the Products Complaint Department. Replacement/Repairment of the product as per terms and conditions will be carried out soon.`; } } class ServiceComplaints extends Complaints { constructor() { super(); if (ServiceComplaints.exists) { return ServiceComplaints.instance; } ServiceComplaints.instance = this; ServiceComplaints.exists = true; return this; } replyMessage({ id, customer, details }) { return `Complaint No. ${id} reported by ${customer} regarding ${details} have been filed with the Service Complaint Department. The issue will be resolved or the purchase will be refunded as per terms and conditions.`; } } // usage const registry = new ComplaintRegistry(); const reportService = registry.registerComplaint("Martha", "service", "availability"); // "Complaint No. 1 reported by Martha regarding availability have been filed with the Service Complaint Department. The issue will be resolved or the purchase will be refunded as per terms and conditions." const reportProduct = registry.registerComplaint("Jane", "product", "faded color"); // "Complaint No. 2 reported by Jane regarding faded color have been filed with the Products Complaint Department. Replacement/Repairment of the product as per terms and conditions will be carried out soon."享元模式
這是一種結構型設計模式,專注于通過細粒度對象進行有效的數據共享。 它用于提高效率和記憶保存目的。
此模式可用于任何類型的緩存目的。 事實上,現代瀏覽器使用享元模式的變體來防止兩次加載相同的圖像。
在下面的例子中,我們創建了一個細粒度的享元類Icecream,用于分享有關冰淇淋口味的數據,以及一個工廠級的IcecreamFactory來創建這些享元類對象。 對于內存保留,如果同一對象被實例化兩次,則對象將被回收。
這是享元實現的一個簡單示例。
// flyweight class class Icecream { constructor(flavour, price) { this.flavour = flavour; this.price = price; } } // factory for flyweight objects class IcecreamFactory { constructor() { this._icecreams = []; } createIcecream(flavour, price) { let icecream = this.getIcecream(flavour); if (icecream) { return icecream; } else { const newIcecream = new Icecream(flavour, price); this._icecreams.push(newIcecream); return newIcecream; } } getIcecream(flavour) { return this._icecreams.find(icecream => icecream.flavour === flavour); } } // usage const factory = new IcecreamFactory(); const chocoVanilla = factory.createIcecream("chocolate and vanilla", 15); const vanillaChoco = factory.createIcecream("chocolate and vanilla", 15); // reference to the same object console.log(chocoVanilla === vanillaChoco); // true代理模式
這是一種結構型設計模式,其行為完全符合其名稱。它充當另一個對象的代理或占位符來控制對它的訪問。
它通常用于目標對象受到約束且可能無法有效地處理其所有職責的情況。在這種情況下,代理通常向客戶機提供相同的接口,并添加一個間接層,以支持對目標對象的受控訪問,以避免對目標對象施加過大的壓力。
在處理網絡請求較多的應用程序時,代理模式非常有用,可以避免不必要的或冗余的網絡請求。
在下面的例子中,我們將使用兩個新的ES6特性,Proxy和Reflect。代理對象用于為JavaScript對象的基本操作定義自定義行為(記住,函數和數組也是JavaScript中的對象)。它是一個可用于創建對象Proxy的構造函數方法。它接受要代理的對象target和定義必要定制的handler對象。handler對象允許定義一些陷阱函數,如get、set、has、apply等,這些函數用于將添加附加到它們的用法中的自定義行為。另一方面,Reflect是一個內置對象,它提供類似于Proxy的handler對象支持的方法作為靜態方法。它不是構造函數; 其靜態方法用于可攔截的JavaScript操作。
現在,我們創建一個函數,它可以看作是一個網絡請求。我們將其命名為networkFetch。它接受一個URL并相應地作出響應。我們希望實現一個代理,在這個代理中,只有在緩存中沒有網絡響應時,我們才能從網絡獲得響應。否則,我們只從緩存返回一個響應。
全局變量cache將存儲緩存的響應。我們創建了一個名為proxiedNetworkFetch的代理,將原始networkFetch作為target,并在對象handler中使用apply方法來代理函數調用。apply方法在對象target本身上傳遞。這個值作為thisArg,參數以類似數組的結構args傳遞給它。
我們檢查傳遞的url參數是否在緩存中。如果它存在于緩存中,我們將從那里返回響應,而不會調用原始目標函數。如果沒有,那么我們使用Reflect.apply方法用thisArg(盡管在我們的例子中它沒有任何意義)來調用函數target和它傳遞的參數。
// Target function networkFetch(url) { return `${url} - Response from network`; } // Proxy // ES6 Proxy API = new Proxy(target, handler); const cache = []; const proxiedNetworkFetch = new Proxy(networkFetch, { apply(target, thisArg, args) { const urlParam = args[0]; if (cache.includes(urlParam)) { return `${urlParam} - Response from cache`; } else { cache.push(urlParam); return Reflect.apply(target, thisArg, args); } }, }); // usage console.log(proxiedNetworkFetch("dogPic.jpg")); // "dogPic.jpg - Response from network" console.log(proxiedNetworkFetch("dogPic.jpg")); // "dogPic.jpg - Response from cache"責任鏈模式
這是一種提供松散耦合對象鏈的行為型設計模式。這些對象中的每一個都可以選擇處理客戶機的請求。
責任鏈模式的一個很好的例子是DOM中的事件冒泡機制,其中一個事件通過一系列嵌套的DOM元素傳播,其中一個元素可能附加了一個“事件偵聽器”來偵聽該事件并對其進行操作。
在下面的例子中,我們創建了一個CumulativeSum,可以使用一個可選的initialValue進行實例化。它有一個方法add,將傳遞的值添加到對象的sum屬性中,并返回object本身,以允許鏈接add方法調用。
在jQuery中也可以看到這種常見的模式,對jQuery對象的幾乎任何方法調用都會返回一個jQuery對象,以便將方法調用鏈接在一起。
class CumulativeSum { constructor(intialValue = 0) { this.sum = intialValue; } add(value) { this.sum += value; return this; } } // usage const sum1 = new CumulativeSum(); console.log(sum1.add(10).add(2).add(50).sum); // 62 const sum2 = new CumulativeSum(10); console.log(sum2.add(10).add(20).add(5).sum); // 45命令模式
這是一種行為型設計模式,旨在將操作或操作封裝為對象。 此模式允許通過將請求操作或調用方法的對象與執行或處理實際實現的對象分離來松散耦合系統和類。
剪貼板交互API有點類似于命令模式。 如果您是Redux用戶,則您已經遇到過命令模式。 使我們能夠回到之前的時間節點的操作不過就是把可以跟蹤以重做或撤消操作封裝起來。 因此,我們實現了時間旅行式調試。
在下面的例子中,我們有一個名為SpecialMath的類,它有多個方法和一個Command類,它封裝了要在其主題上執行的命令,即SpecialMath類的一個對象。Command類還跟蹤所有已執行的命令,這些命令可用于擴展其功能以包括撤消和重做類型操作。
class SpecialMath { constructor(num) { this._num = num; } square() { return this._num ** 2; } cube() { return this._num ** 3; } squareRoot() { return Math.sqrt(this._num); } } class Command { constructor(subject) { this._subject = subject; this.commandsExecuted = []; } execute(command) { this.commandsExecuted.push(command); return this._subject[command](); } } // usage const x = new Command(new SpecialMath(5)); x.execute("square"); x.execute("cube"); console.log(x.commandsExecuted); // ["square", "cube"]迭代器模式
它是一種行為型設計模式,提供了一種方法來順序訪問聚合對象的各個元素,而無需暴露其內部表示。
迭代器有一種特殊的行為,在這種行為中,我們通過調用next()一次遍歷一組有序的值,直到到達末尾。在ES6中引入迭代器和生成器使得迭代器模式的實現非常簡單。
下面有兩個例子。首先,一個IteratorClass使用iterator規范,而另一個iteratorUsingGenerator使用生成器函數。
Symbol.iterator(Symbol-一種新的基本數據類型)用于指定對象的默認迭代器。它必須被定義為一個集合,以便能夠使用for...of的循環結構。在第一個示例中,我們定義構造函數來存儲一些數據集合,然后定義符號Symbol.iterator,它返回一個對象和用于迭代的next方法。
對于第二種情況,我們定義了一個生成器函數,將數據數組傳遞給它,并使用next和yield迭代地返回它的元素。生成器函數是一種特殊類型的函數,它充當迭代器的工廠,可以迭代地顯式維護自己的內部狀態和生成值。它可以暫停并恢復自己的執行周期。
它是一種行為型設計模式,封裝了一組對象如何相互交互。它通過促進松散耦合,防止對象彼此顯式引用,從而為一組對象提供了中央權限。
在下面的例子中,我們使用TrafficTower作為中介來控制Airplane對象之間的交互方式。所有Airplane對象都將自己注冊到一個TrafficTower對象中,而中介類對象處理Airplane對象如何接收所有其他Airplane對象的坐標數據。
class TrafficTower { constructor() { this._airplanes = []; } register(airplane) { this._airplanes.push(airplane); airplane.register(this); } requestCoordinates(airplane) { return this._airplanes.filter(plane => airplane !== plane).map(plane => plane.coordinates); } } class Airplane { constructor(coordinates) { this.coordinates = coordinates; this.trafficTower = null; } register(trafficTower) { this.trafficTower = trafficTower; } requestCoordinates() { if (this.trafficTower) return this.trafficTower.requestCoordinates(this); return null; } } // usage const tower = new TrafficTower(); const airplanes = [new Airplane(10), new Airplane(20), new Airplane(30)]; airplanes.forEach(airplane => { tower.register(airplane); }); console.log(airplanes.map(airplane => airplane.requestCoordinates())) // [[20, 30], [10, 30], [10, 20]]觀察者模式
它是一個至關重要的行為型設計模式,定義了對象之間一對多的依賴關系,這樣當一個對象(發布方)更改其狀態時,所有其他依賴對象(訂閱者)都會得到通知并自動更新。這也稱為PubSub(發布者/訂閱者)或事件分派器/偵聽器模式。發布者有時稱為主題,訂閱者有時稱為觀察者。
如果您曾經使用addEventListener或jQuery的.on編寫過偶數處理代碼,那么您可能已經對這種模式有些熟悉了。它在反應性編程(就像RxJS)中也有影響。
在本例中,我們創建了一個簡單的Subject類,該類具有從訂閱者集合中添加和刪除Observer類對象的方法。此外,還提供一個fire方法,用于將Subject類對象中的任何更改傳播給訂閱的觀察者。另一方面,Observer類有自己的內部狀態,以及基于訂閱的Subject傳播的更改更新內部狀態的方法。
是一種行為型設計模式,允許對象根據其內部狀態的變化來改變其行為。狀態模式類返回的對象似乎改變了它的類。它為有限的一組對象提供特定于狀態的邏輯,其中每個對象類型表示特定的狀態。
我們將以交通燈為例來理解這種模式。TrafficLight類根據其內部狀態(Red、Yellow或Green類的對象)更改返回的對象。
它是一種行為型設計模式,允許為特定任務封裝替代算法。它定義了一系列算法,并以一種可以在運行時互換的方式封裝它們,而不需要客戶機的干涉或知識。
在下面的示例中,我們創建了一個Commute類,用于封裝所有可能的通勤策略。然后,我們定義了三種策略,即Bus、PersonalCar和Taxi。使用此模式,我們可以在運行時將實現替換為Commute對象的travel方法。
這是一種行為型設計模式,基于定義算法的框架或操作的實現,但將一些步驟推遲到子類。它允許子類在不改變算法外部結構的情況下重新定義算法的某些步驟。
在本例中,我們有一個模板類Employee,它部分實現了work方法。子類實現職責方法是為了使它作為一個整體發揮作用。然后,我們創建了兩個子類Developer和Tester,它們擴展了模板類并實現了所需的方法。
設計模式對軟件工程至關重要,也對我們解決常見問題非常有幫助。 但這是一個非常廣泛的主題,并且根本不可能在短短的一篇文章中囊括關于它們的所有內容。 因此,我選擇簡短而簡潔地談論我認為在編寫現代JavaScript時非常方便的那些。 為了深入了解,我建議你看一下這些書:
《設計模式:可復用面向對象軟件的基礎》,作者:Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides(即“四人幫”)
《JavaScript設計模式》,作者:Addy Osmani
《JavaScript模式》,作者:Stoyan Stefanov
《JavaScript模式》,作者:Stoyan Stefanov
點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)
關注公眾號「新前端社區」,號享受文章首發體驗!每周重點攻克一個前端技術難點。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106611.html
摘要:簡單工廠模式的實質是由一個工廠類根據傳入的參數,動態決定應該創建哪一個產品類。中的就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得對象,但是否是在傳入參數后創建還是傳入參數前創建這個要根據具體情況來定。 設計模式作為工作學習中的枕邊書,卻時常處于勤說不用的尷尬境地,也不是我們時常忘記,只是一直沒有記憶。 Spring作為業界的經典框架,無論是在架構設計方面,還是在代碼編寫方面,都堪...
摘要:簡單工廠模式的實質是由一個工廠類根據傳入的參數,動態決定應該創建哪一個產品類。中的就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得對象,但是否是在傳入參數后創建還是傳入參數前創建這個要根據具體情況來定。 設計模式作為工作學習中的枕邊書,卻時常處于勤說不用的尷尬境地,也不是我們時常忘記,只是一直沒有記憶。 Spring作為業界的經典框架,無論是在架構設計方面,還是在代碼編寫方面,都堪...
摘要:跟相反,該元素可以向上查詢,也就是可以查詢到父元素就跟原生微信小程序一樣,能獲取標簽上以為前綴的屬性集合蜘蛛俠蜘蛛俠注意這是開發規范問題,凡是自定義屬性都要加上前綴假設瀏覽器的參數是蜘蛛俠蜘蛛俠這是一個對象,該對象里封裝了許多操作元 closest 跟querySelector相反,該元素可以向上查詢,也就是可以查詢到父元素: document.querySelector(li...
摘要:雖然今年沒有換工作的打算但為了跟上時代的腳步還是忍不住整理了一份最新前端知識點知識點匯總新特性,語義化瀏覽器的標準模式和怪異模式和的區別使用的好處標簽廢棄的標簽,和一些定位寫法放置位置和原因什么是漸進式渲染模板語言原理盒模型,新特性,偽 雖然今年沒有換工作的打算 但為了跟上時代的腳步 還是忍不住整理了一份最新前端知識點 知識點匯總1.HTMLHTML5新特性,語義化瀏覽器的標準模式和怪...
閱讀 1863·2023-04-26 02:46
閱讀 1996·2021-11-25 09:43
閱讀 1140·2021-09-29 09:35
閱讀 2096·2019-08-30 15:56
閱讀 3419·2019-08-30 15:54
閱讀 2627·2019-08-29 16:35
閱讀 3116·2019-08-29 15:25
閱讀 3282·2019-08-29 14:01