摘要:例如可以用構造函數實現單例模式。例如當這個構造函數被調用,它會檢查是否存在。工廠模式工廠模式使用工廠方法創建對象,而不指定所創建對象的確切類或構造函數。雖然了解審設計模式很重要,但是不要過度使用它們。
原文
當你開始了一個新項目,你不會馬上開始編寫代碼。第一步,你必須定義這個項目解決什么問題和適用范圍,然后列出這個項目的特性或者規格。在你開始編碼或者正在處理更復雜的項目之后,你應該選擇最合適你項目的設計模式。
什么是設計模式在軟件工程里,設計模式是軟件設計的一種常見問題的可重用解決方案。設計模式是經驗豐富的軟件開發人員所使用的最佳實踐,可以認為是編程模版。
為什么使用設計模式許多程序員要么認為使用設計模式是浪費時間的,要么他們不知道如何正確使用設計模式。但是正確使用了設計模式會幫助你寫出可維護性高的代碼。
最重要的是,設計模式給軟件開發者共同的話題、術語。會讓學習了設計模式的初學者更快看懂你寫的代碼。
舉個例子,如果你在項目中使用了裝飾器模式,那么新來的程序員馬上知道你這段代碼正在做什么(譯者:前提是這名程序員知道這個設計模式),并且他們會更加專注解決業務問題,而不是試圖理解這段代碼做的是什么。
現在我們知道什么是設計模式,并且知道了它們為什么那么重要,讓我們開始深入各個應用在js的設計模式吧。
模塊是一段獨立的代碼,因此我們可以在不影響其他代碼的情況下修改模塊的代碼。模塊還允許我們通過變量來創建多帶帶的范圍以避免命名空間的污染。當模塊與其它代碼耦合度低時(譯者:類似用依賴導入第三方庫的那種程度),我們還可以在其他項目復用模塊。
模塊是任何js應用程序不可或缺的一部分,有助于保持代碼高內聚低耦合。有許多方法可以在JavaScript中創建模塊,其中一種是模塊模式。
不同于其它的編程語言,js沒有修飾符,也就是說你無法將變量聲明為私有或公有。因此Module模式也用于模擬封裝的概念。
我們可以在js使用IIFE(立即調用函數表達式)、閉包、函數范圍來實現模塊。例子如下:
const myModule = (function() { const privateVariable = "Hello World"; function privateMethod() { console.log(privateVariable); } return { publicMethod: function() { privateMethod(); } } })(); myModule.publicMethod();
由于是IIFE,代碼立即執行,并返回對象給myModule對象。由于是閉包,即使IIFE執行完成,但是返回的對象依然能夠訪問IIFE內定義的函數和變量。
因為,在IIFE內定義的變量和函數基本上在外部不能直接調用的,可以看作是myModule的私有的。
代碼執行后,myModule變量像這樣:
const myModule = { publicMethod: function() { privateMethod(); }};
因此,我們可以調用publicMethod方法,而它有調用privateMethod方法。例如:
// Prints "Hello World" module.publicMethod();揭示模塊模式
揭示模塊模式是經Christian Heilmann基于模塊模式略微改進的一種模式。模塊模式的問題是我們必須創建公共的方法,然后才能通過這個公共的方法來調用私有變量和方法。在這個模式中,我們將返回對象的屬性映射到我們想要暴露出去的私有方法。這就是揭示模塊模式。例如:
const myRevealingModule = (function() { let privateVar = "Peter"; const publicVar = "Hello World"; function privateFunction() { console.log("Name: "+ privateVar); } function publicSetName(name) { privateVar = name; } function publicGetName() { privateFunction(); } /** reveal methods and variables by assigning them to object properties */ return { setName: publicSetName, greeting: publicVar, getName: publicGetName }; })(); myRevealingModule.setName("Mark"); // prints Name: Mark myRevealingModule.getName();
這種模式讓我們更容易理解我們可以公開訪問那些方法和變量,這有助于提高代碼可讀性。
代碼執行后,myRevealingModule像這樣:
const myRevealingModule = { setName: publicSetName, greeting: publicVar, getName: publicGetName };
我們可以調用myRevealingModule.setName("Mark"),它是對內部publicSetName的引用,而myRevealingModule.getName()的引用是對內部publicGetName方法。例如:
myRevealingModule.setName("Mark"); // prints Name: Mark myRevealingModule.getName();對比普通模塊模式,揭示模塊模式的優點如下:
通過修改return語句中的一行代碼,我們可以將成員從public更改為private,反之亦然。
返回的對象里不包含函數定義,所有右側表達式都在IIFE中定義了,代碼清晰易讀。
ES6模塊在es6之前,js沒有模塊,所以開發者不得不第三方庫或者模塊模式來實現模塊。但在es6,js有了自己模塊實現。
ES6的模塊用文件來存儲。每個文件只能有一個模塊。默認情況下,模塊內的所有內容都是私有的。開發者可以使用export關鍵字公開函數、變量和類。模塊內的代碼始終以嚴格模式運行。
輸出模塊有兩種方式輸出函數和變量聲明:
在函數和變量的前面使用export關鍵字。例如:
// utils.js
export const greeting = "Hello World";
export function sum(num1, num2) {
console.log("Sum:", num1, num2);
return num1 + num2;
}
export function subtract(num1, num2) {
console.log("Subtract:", num1, num2);
return num1 - num2;
}
// This is a private function
function privateLog() {
console.log("Private Function");
}
在代碼的末尾加上export關鍵字,導出我們要公開的函數和變量:
// utils.js
function multiply(num1, num2) {
console.log("Multiply:", num1, num2);
return num1 * num2;
}
function divide(num1, num2) {
console.log("Divide:", num1, num2);
return num1 / num2;
}
// This is a private function
function privateLog() {
console.log("Private Function");
}
export {multiply, divide};
跟導出模塊差不多,也有兩種方法導入模塊:
導入特定的方法
// main.js
// importing multiple items
import { sum, multiply } from "./utils.js";
console.log(sum(3, 7));
console.log(multiply(3, 7));
導入所有
// main.js
// importing all of module
import * as utils from "./utils.js";
console.log(utils.sum(3, 7));
console.log(utils.multiply(3, 7));
如果要避免命名沖突,可以在導出和導入期間更改導出的名稱。例如:
重命名導出的方法
// utils.js function sum(num1, num2) { console.log("Sum:", num1, num2); return num1 + num2; } function multiply(num1, num2) { console.log("Multiply:", num1, num2); return num1 * num2; } export {sum as add, multiply};
重命名導入的方法
// main.js import { add, multiply as mult } from "./utils.js"; console.log(add(3, 7)); console.log(mult(3, 7));單例模式
單例指的是只能實例化一次的對象。如果不存在,則單例模式會創建類的新實例。 如果存在實例,則它只返回對該對象的引用。 對構造函數的任何重復調用總是會獲取相同的對象。
js支持單例模式,它有關于單例模式的實現。在js我們不應該叫單例,應該叫對象字面量。例如:
const user = { name: "Peter", age: 25, job: "Teacher", greet: function() { console.log("Hello!"); } };
因為在js中,每個對象占用一個唯一的內存位置,當我們調用用戶對象時,實際上返回的是該對象的引用。
如果我們嘗試復制user對象到另一個變量,并且修改其中的值。例如:
const user1 = user; user1.name = "Mark";
我們會看到兩個對象都被修改,因為js中的對象是引用而不是值傳遞。所以內存中只有一個對象。例如:
// prints "Mark" console.log(user.name); // prints "Mark" console.log(user1.name); // prints true console.log(user === user1);
可以用構造函數實現單例模式。例如:
let instance = null; function User() { if(instance) { return instance; } instance = this; this.name = "Peter"; this.age = 25; return instance; } const user1 = new User(); const user2 = new User(); // prints true console.log(user1 === user2);
當這個構造函數被調用,它會檢查instance是否存在。如果對象不存在,它會分配值給instance變量。如果存在,返回已存在的對象。
也可以使用模塊模式實現單例。例如:
const singleton = (function() { let instance; function init() { return { name: "Peter", age: 24, }; } return { getInstance: function() { if(!instance) { instance = init(); } return instance; } } })(); const instanceA = singleton.getInstance(); const instanceB = singleton.getInstance(); // prints true console.log(instanceA === instanceB);
在上面的代碼中,我們用singleton.getInstance方法創建了新的instance實例。如果實例已存在,則此方法返回已存在實例,如果實例不存在,則通過調用init()函數創建新實例。
工廠模式工廠模式使用工廠方法創建對象,而不指定所創建對象的確切類或構造函數。
工廠模式用于創建對象,不暴露實例化邏輯。當我們需要根據特定條件生成不同的對象時,可以使用此模式。例如:
class Car{ constructor(options) { this.doors = options.doors || 4; this.state = options.state || "brand new"; this.color = options.color || "white"; } } class Truck { constructor(options) { this.doors = options.doors || 4; this.state = options.state || "used"; this.color = options.color || "black"; } } class VehicleFactory { createVehicle(options) { if(options.vehicleType === "car") { return new Car(options); } else if(options.vehicleType === "truck") { return new Truck(options); } } }
我創建了car和truck類(帶有一些默認值),用于創建新的汽車和卡車對象。然后我定義了VehicleFactory類,用于根據對象中收到的vehicleType屬性創建并返回一個新對象。
const factory = new VehicleFactory(); const car = factory.createVehicle({ vehicleType: "car", doors: 4, color: "silver", state: "Brand New" }); const truck= factory.createVehicle({ vehicleType: "truck", doors: 2, color: "white", state: "used" }); // Prints Car {doors: 4, state: "Brand New", color: "silver"} console.log(car); // Prints Truck {doors: 2, state: "used", color: "white"} console.log(truck);
我是實例化了VehicleFactory類,之后我們調用factory.createVehicle方法創建car和truck對象,并且傳遞vehicleType屬性值為car或truck的options對象。
裝飾器模式裝飾器模式用于擴展對象的功能,而無需修改現有的類或構造函數。此模式可用于向對象添加新功能,無需修改它們的基礎代碼。
一個簡單的例子:
function Car(name) { this.name = name; // Default values this.color = "White"; } // Creating a new Object to decorate const tesla= new Car("Tesla Model 3"); // Decorating the object with new functionality tesla.setColor = function(color) { this.color = color; } tesla.setPrice = function(price) { this.price = price; } tesla.setColor("black"); tesla.setPrice(49000); // prints black console.log(tesla.color);
這種模式更實際的例子是:
比方說,汽車的成本取決于它的功能數量。 如果沒有裝飾器模式,我們必須為不同的功能組合創建不同的類,每個類都有一個成本方法來計算成本。例如:
class Car() { } class CarWithAC() { } class CarWithAutoTransmission { } class CarWithPowerLocks { } class CarWithACandPowerLocks { }
但有了裝飾器模式,我們可以基于car類創建對象,并且使用裝飾器函數添加不同的方法。例如:
class Car { constructor() { // Default Cost this.cost = function() { return 20000; } } } // Decorator function function carWithAC(car) { car.hasAC = true; const prevCost = car.cost(); car.cost = function() { return prevCost + 500; } } // Decorator function function carWithAutoTransmission(car) { car.hasAutoTransmission = true; const prevCost = car.cost(); car.cost = function() { return prevCost + 2000; } } // Decorator function function carWithPowerLocks(car) { car.hasPowerLocks = true; const prevCost = car.cost(); car.cost = function() { return prevCost + 500; } }
首先,我們創建了用于創建car對象的基類Car。然后,我們為要添加到其上的要素創建裝飾器,并將Car對象作為參數傳遞。 然后我們覆蓋該對象的cost函數,該函數返回汽車的更新成本,并向該對象添加新屬性以指示添加了哪個特征。
為了添加新功能,我們可以這樣做:
const car = new Car(); console.log(car.cost()); carWithAC(car); carWithAutoTransmission(car); carWithPowerLocks(car);
最后,我們這樣子計算成本:
// Calculating total cost of the car console.log(car.cost());結論
我們已經了解部分設計模式在js中的實現,但是還有部分在本文沒有涉及。
雖然了解審設計模式很重要,但是不要過度使用它們。在使用某個設計模式之前,應該考慮是否能解決你的痛點。要了解模式是否適合你,你應該研究設計模式以及該設計模式的應用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98808.html
摘要:全文為這些年,我曾閱讀深入理解過或正在閱讀學習即將閱讀的一些優秀經典前端后端書籍。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 全文為這些年,我曾閱讀、深入理解過(或正在閱讀學習、即將閱讀)的一些優秀經典前端/Java后端書籍。全文為純原創,且將持續更新,未經許可,不得進行轉載。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 基礎 基礎書籍 進階 進階階段,深入學習的書...
摘要:全文為這些年,我曾閱讀深入理解過或正在閱讀學習即將閱讀的一些優秀經典前端后端書籍。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 全文為這些年,我曾閱讀、深入理解過(或正在閱讀學習、即將閱讀)的一些優秀經典前端/Java后端書籍。全文為純原創,且將持續更新,未經許可,不得進行轉載。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 基礎 基礎書籍 進階 進階階段,深入學習的書...
摘要:全文為這些年,我曾閱讀深入理解過或正在閱讀學習即將閱讀的一些優秀經典前端后端書籍。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 全文為這些年,我曾閱讀、深入理解過(或正在閱讀學習、即將閱讀)的一些優秀經典前端/Java后端書籍。全文為純原創,且將持續更新,未經許可,不得進行轉載。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 基礎 基礎書籍 進階 進階階段,深入學習的書...
摘要:全文為這些年,我曾閱讀深入理解過或正在閱讀學習即將閱讀的一些優秀經典前端后端書籍。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 全文為這些年,我曾閱讀、深入理解過(或正在閱讀學習、即將閱讀)的一些優秀經典前端/Java后端書籍。全文為純原創,且將持續更新,未經許可,不得進行轉載。當然,如果您喜歡這篇文章,可以動手點點贊或者收藏。 基礎 基礎書籍 進階 進階階段,深入學習的書...
閱讀 1172·2021-11-24 09:39
閱讀 2675·2021-09-28 09:35
閱讀 1070·2019-08-30 15:55
閱讀 1361·2019-08-30 15:44
閱讀 880·2019-08-29 17:00
閱讀 1969·2019-08-29 12:19
閱讀 3311·2019-08-28 18:28
閱讀 690·2019-08-28 18:10