溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript 的7種設(shè)計(jì)模式分別是哪些

發(fā)布時(shí)間:2021-09-30 10:55:53 來源:億速云 閱讀:104 作者:柒染 欄目:web開發(fā)

JavaScript 的7種設(shè)計(jì)模式分別是哪些,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

當(dāng)啟動(dòng)一個(gè)新的項(xiàng)目時(shí)候,我們不應(yīng)該馬上開始編程。而是首先應(yīng)該定義項(xiàng)目的目的和范圍,然后列出其功能或規(guī)格。如果你已經(jīng)開始編程或者正在從事一個(gè)復(fù)雜的項(xiàng)目,則應(yīng)該選擇一個(gè)最適合你項(xiàng)目的設(shè)計(jì)模式。

什么是設(shè)計(jì)模式?

在軟件工程中,設(shè)計(jì)模式是針對(duì)軟件設(shè)計(jì)中常見問題的可重用解決方案。設(shè)計(jì)模式也是經(jīng)驗(yàn)豐富的開發(fā)人員針對(duì)特定問題的最佳實(shí)踐。它可以被當(dāng)作編程的模板。

為什么要使用設(shè)計(jì)模式?

許多工程師要么認(rèn)為設(shè)計(jì)模式浪費(fèi)時(shí)間,要么不知道如何恰當(dāng)?shù)氖褂迷O(shè)計(jì)模式。但如果能正確使用設(shè)計(jì)模式,則可以幫助你寫出更好的可讀性更高的代碼,并且代碼更容易被維護(hù)和理解。

最重要的是,設(shè)計(jì)模式為軟件開發(fā)人員提供了通用的詞匯表。它們能讓學(xué)習(xí)你代碼的人很快了解代碼的意圖。例如,如果你的項(xiàng)目中使用了裝飾器模式,那么新的開發(fā)可以很快就知道這段代碼的作用,從而他們可以將更多精力放在解決業(yè)務(wù)問題上,而不是試圖理解代碼在做什么。

我們已經(jīng)知道了什么是設(shè)計(jì)模式和它的重要性,下面我們深入研究一下 JavaScript 中的 7 種設(shè)計(jì)模式。

一、模塊模式

模塊是一段獨(dú)立的代碼,因此我們可以更新模塊而不會(huì)影響代碼的其它部分。模塊還允許我們通過為變量創(chuàng)建單獨(dú)的作用域來避免命名空間污染。當(dāng)它們與其它代碼解耦時(shí),我們還可以在其它項(xiàng)目中重用模塊。

模塊是任何現(xiàn)代 JavaScript 應(yīng)用程序不可或缺的一部分,有助于保持代碼干凈,獨(dú)立和有條理。在 JavaScript  中有許多方法可以創(chuàng)建模塊,其中一種是模塊模式。

與其它編程語言不同,JavaScript 沒有訪問修飾符,也就是說,你不能將變量聲明為私有的或公共的。因此,模塊模式也可用來模擬封裝的概念。

模塊模式使用 IIFE(立即調(diào)用的函數(shù)表達(dá)式),閉包和函數(shù)作用域來模擬封裝的概念。例如:

const myModule = (function() {     const privateVariable = 'Hello World';     function privateMethod() {     console.log(privateVariable);   }   return {     publicMethod: function() {       privateMethod();     }   } })(); myModule.publicMethod();

由于是 IIFE 因此代碼會(huì)被立即執(zhí)行,并將返回對(duì)象賦值給了 myModule 變量。由于閉包,即使在 IIFE 完成后,返回的對(duì)象仍可以訪問 IIFE  內(nèi)部定義的函數(shù)和變量。

因此,IIFE 內(nèi)部定義的變量和函數(shù)對(duì)外部是看不見的,從而使其成為 myModule 模塊的私有成員。

執(zhí)行代碼后,myModule 變量看起來像下面所示:

const myModule = {   publicMethod: function() {     privateMethod();   }};

因此當(dāng)我們調(diào)用 publicMethod() 時(shí)候,它將調(diào)用 privateMethod() 例如:

// Prints 'Hello World' module.publicMethod();

二、揭示模塊模式

揭示模塊模式是 Christian Heilmann  對(duì)模塊模式的略微改進(jìn)。模塊模式的問題在于,我們必須創(chuàng)建新的公共函數(shù)才能調(diào)用私有函數(shù)和變量。

在這種模式下,我們將返回的對(duì)象的屬性映射到要公開暴露的私有函數(shù)上。這就是為什么將其稱為揭示模塊模式。例如:

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();

這種模式讓我們更容易知道哪些函數(shù)和變量是公共的,無形中提高了代碼的可讀性。執(zhí)行代碼后 myRevealingModule 看起來像下所示:

const myRevealingModule = {   setName: publicSetName,   greeting: publicVar,   getName: publicGetName };

當(dāng)我們調(diào)用 myRevealingModule.setName('Mark') 時(shí),實(shí)際調(diào)用了內(nèi)部的 publicSetName。當(dāng)調(diào)用  myRevealingModule.getName() 時(shí),實(shí)際調(diào)用了內(nèi)部的 publicGetName 例如:

myRevealingModule.setName('Mark'); // prints Name: Mark myRevealingModule.getName();

與模塊模式相比,揭示模塊模式的優(yōu)勢(shì)有:

  • 通過修改 return 語句中的一行,我們可以將成員從公共變?yōu)闉樗饺?,反之亦然?/p>

  • 返回的對(duì)象不包含任何函數(shù)定義,所有右側(cè)表達(dá)式都在 IIFE 中定義,從而使代碼清晰易讀。

三、ES6 模塊

在 ES6 之前,JavaScript 沒有內(nèi)置模塊,因此開發(fā)人員必須依靠第三方庫或模塊模式來實(shí)現(xiàn)模塊。但是自從  ES6,JavaScript 內(nèi)置了模塊。

ES6 的模塊是以文件形式存儲(chǔ)的。每個(gè)文件只能有一個(gè)模塊。默認(rèn)情況下,模塊內(nèi)的所有內(nèi)容都是私有的。通過使用 export  關(guān)鍵字來暴露函數(shù)、變量和類。模塊內(nèi)的代碼始終在嚴(yán)格模式下運(yùn)行。

3.1 導(dǎo)出模塊

有兩種方法可以導(dǎo)出函數(shù)和變量聲明:

  • 在函數(shù)和變量聲明的前面添加 export 關(guān)鍵字。例如:

// 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 關(guān)鍵字來暴露函數(shù)和變量。例如:

// 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};

3.2 導(dǎo)入模塊

與導(dǎo)出模塊相似,有兩種使用 import 關(guān)鍵字導(dǎo)入模塊的方法。例如:

  • 一次導(dǎo)入多個(gè)項(xiàng)目

// main.js // importing multiple items import { sum, multiply } from './utils.js'; console.log(sum(3, 7)); console.log(multiply(3, 7));
  • 導(dǎo)入所有模塊

// main.js // importing all of module import * as utils from './utils.js'; console.log(utils.sum(3, 7)); console.log(utils.multiply(3, 7));

3.3 導(dǎo)入導(dǎo)出中使用別名

  • 重命名導(dǎo)出

// 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};
  • 重命名導(dǎo)入

// main.js import { add, multiply as mult } from './utils.js'; console.log(add(3, 7)); console.log(mult(3, 7));

四、單例模式

一個(gè)單例對(duì)象是只能實(shí)例化一次的對(duì)象。如果不存在,則單例模式將創(chuàng)建類的新實(shí)例。如果存在實(shí)例,則僅返回對(duì)該對(duì)象的引用。重復(fù)調(diào)用構(gòu)造函數(shù)將始終獲取同一對(duì)象。

JavaScript 是一直內(nèi)置單例的語言。我們只是不稱它們?yōu)閱卫?,我們稱它們?yōu)閷?duì)象字面量。例如:

const user = {   name: 'Peter',   age: 25,   job: 'Teacher',   greet: function() {     console.log('Hello!');   } };

因?yàn)?JavaScript 中的每個(gè)對(duì)象都占用一個(gè)唯一的內(nèi)存位置,并且當(dāng)我們調(diào)用該 user 對(duì)象時(shí),實(shí)際上是在返回該對(duì)象的引用。

如果我們嘗試將 user 變量復(fù)制到另一個(gè)變量并修改該變量。例如:

const user1 = user; user1.name = 'Mark';

我們將看到兩個(gè)對(duì)象都被修改,因?yàn)?JavaScript 中的對(duì)象是通過引用而不是通過值傳遞的。因此,內(nèi)存中只有一個(gè)對(duì)象。例如:

// prints 'Mark' console.log(user.name); // prints 'Mark' console.log(user1.name); // prints true console.log(user === user1);

可以使用構(gòu)造函數(shù)來實(shí)現(xiàn)單例模式。例如:

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

調(diào)用此構(gòu)造函數(shù)時(shí),它將檢查 instance 對(duì)象是否存在。如果對(duì)象不存在,則將 this 變量分配給 instance  變量。如果該對(duì)象存在,則只返回該對(duì)象。

單例也可以使用模塊模式來實(shí)現(xiàn)。例如:

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

在上面的代碼中,我們通過調(diào)用 singleton.getInstance  方法來創(chuàng)建一個(gè)新實(shí)例。如果實(shí)例已經(jīng)存在,則此方法僅返回該實(shí)例。如果該實(shí)例不存在,則通過調(diào)用該 init() 函數(shù)創(chuàng)建一個(gè)新實(shí)例。

五、工廠模式

工廠模式使用工廠方法創(chuàng)建對(duì)象而不需要指定具體的類或構(gòu)造函數(shù)的模式。

工廠模式用于創(chuàng)建對(duì)象而不需要暴露實(shí)例化的邏輯。當(dāng)我們需要根據(jù)特定條件生成不同的對(duì)象時(shí),可以使用此模式。例如:

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);       }   } }

這里,創(chuàng)建了一個(gè) Car 和一個(gè) Truck 類(具有一些默認(rèn)值),該類用于創(chuàng)建新的 car 和  truck對(duì)象。而且定義了一個(gè)VehicleFactory 類,用來根據(jù) options 對(duì)象中的 vehicleType 屬性來創(chuàng)建和返回新的對(duì)象。

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 創(chuàng)建了一個(gè)新的 factory 對(duì)象。然后,我們通過調(diào)用 factory.createVehicle 方法并且傳遞  options 對(duì)象,其 vehicleType 屬性可能為 car 或者 truck 來創(chuàng)建新 Car 或 Truck 對(duì)象。

六、裝飾器模式

裝飾器模式用于擴(kuò)展對(duì)象的功能,而無需修改現(xiàn)有的類或構(gòu)造函數(shù)。此模式可用于將特征添加到對(duì)象中,而無需修改底層的代碼。

此模式的一個(gè)簡(jiǎn)單示例為:

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

這種模式的一個(gè)更實(shí)際的例子是:

假設(shè)汽車的成本取決于其功能的數(shù)量。如果沒有裝飾器模式,我們將不得不為不同的功能組合創(chuàng)建不同的類,每個(gè)類都有一個(gè) cost 方法來計(jì)算成本。例如:

class Car() { }  class CarWithAC() { }  class CarWithAutoTransmission { }  class CarWithPowerLocks { }  class CarWithACandPowerLocks { }

但是,通過裝飾器模式,我們可以創(chuàng)建一個(gè)基類 car 并且通過裝飾器函數(shù)給不同的對(duì)象添加對(duì)應(yīng)的成本邏輯。

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

首先,我們創(chuàng)建了小轎車的基類 Car。然后針對(duì)要添加的特性創(chuàng)建了裝飾器并且此裝飾器以 Car  對(duì)象為參數(shù)。然后通過返回更新后的小汽車成本來覆蓋對(duì)象的成本函數(shù),且添加了一個(gè)用來標(biāo)識(shí)某個(gè)特性是否已經(jīng)被添加的屬性。

要添加新的功能,我們只需要像下面一樣就可以:

const car = new Car(); console.log(car.cost()); carWithAC(car); carWithAutoTransmission(car); carWithPowerLocks(car);

最后,我們可以像這樣計(jì)算汽車的成本:

// Calculating total cost of the car console.log(car.cost());

我們已經(jīng)了解了 JavaScript 中使用的各種設(shè)計(jì)模式,但是這里沒有涉及到可以用 JavaScript 實(shí)現(xiàn)的設(shè)計(jì)模式。

盡管了解各種設(shè)計(jì)模式很重要,但不要過度使用它們也同樣重要。在使用設(shè)計(jì)模式之前,你應(yīng)該仔細(xì)考慮你的問題是否適合該設(shè)計(jì)模式。要知道某個(gè)模式是否適合你的問題,應(yīng)該好好研究該設(shè)計(jì)模式以及它的應(yīng)用。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI