目錄
高級前端工程師必備的 JS 設計模式入門教程,常用設計模式案例分享
一、什么是設計模式?為什么前端也要學?
1、設計模式是什么
2、設計模式的產出
二、設計模式在 JS 里的分類
三、常用設計模式實戰講解
1、單例模式(Singleton)
2、工廠模式(Factory)
3、觀察者模式(Observer)
4、代理模式(Proxy)
5、策略模式(Strategy)
6、建造者模式、適配器模式、裝飾器模式和狀態模式
①建造者模式(Builder Pattern)
②適配器模式(Adapter Pattern)
③裝飾器模式(Decorator Pattern)
④狀態模式(State Pattern)
四、結語
????????作者:watermelo37
? ? ? ? CSDN萬粉博主、華為云云享專家、阿里云專家博主、騰訊云、支付寶合作作者,全平臺博客昵稱watermelo37。
? ? ? ? 一個假裝是giser的coder,做不只專注于業務邏輯的前端工程師,Java、Docker、Python、LLM均有涉獵。
---------------------------------------------------------------------
溫柔地對待溫柔的人,包容的三觀就是最大的溫柔。
---------------------------------------------------------------------
高級前端工程師必備的 JS 設計模式入門教程,常用設計模式案例分享
本文適合有JavaScript基礎 、?Vue/React 開發經驗,但對設計模式不太熟悉的前端開發工程師。
????????什么是設計模式?為什么要有設計模式?單例模式、工廠模式、建造者模式、適配器模式、代理模式、裝飾器模式、觀察者模式、策略模式、狀態模式分別是什么?他們解決了什么樣的問題?本文將給您答案。
一、什么是設計模式?為什么前端也要學?
1、設計模式是什么
????????設計模式,就是程序員們總結出來的一套解決常見代碼問題的方法論。
-
它不是死板的規定,不是必須按部就班執行。
-
它是經驗總結,告訴你在遇到某些類型的問題時,如何寫出更優雅、更健壯、更可擴展的代碼。
-
后端用,前端也用,尤其是當你的項目越來越大、多人協作越來越復雜時,設計模式就越重要。
????????類比一下:設計模式就像打游戲時的「固定套路」,讓你在遇到不同怪物時不用每次重頭想招數,而是有一套拿得出手的打法。
2、設計模式的產出
????????設計模式的產出不是特定代碼片段,而是一套可復用的設計思路或結構。
-
有些模式產出一個「結構」(比如單例產出一個唯一對象)。
-
有些模式產出一個「流程/機制」(比如觀察者產出的是發布-訂閱機制)。
-
也有些是組合型的,既有對象也有通信邏輯。
????????設計模式產出的是「結構 + 行為」的組合,沒有嚴格固定形式,但背后有固定的“意圖”或“問題場景”。我們關注的就是其“意圖”和“場景”。
二、設計模式在 JS 里的分類
????????設計模式很多,但最重要的,可以分成三大類:
類型 | 解釋 | 代表模式 |
---|---|---|
創建型 | 解決「如何創建對象」的問題 | 單例模式、工廠模式、建造者模式 |
結構型 | 解決「對象之間怎么組織結構」的問題 | 適配器模式、代理模式、裝飾器模式 |
行為型 | 解決「對象之間怎么協作通信」的問題 | 觀察者模式、策略模式、狀態模式 |
三、常用設計模式實戰講解
1、單例模式(Singleton)
? ? ? ? 定義:整個系統只存在一個實例對象,并且能全局訪問。
? ? ? ? 舉個例子,Vuex里面的store就是單例,事件總線(Event Bus)也是單例。
? ? ? ? 來做個簡單的實現,它通過一個靜態屬性 instance 儲存實例,并在重復創建時返回已有實例,這樣就能實現單例的效果:
class Store {constructor() {if (Store.instance) {return Store.instance;}this.state = {};Store.instance = this;}
}const a = new Store();
const b = new Store();console.log(a === b); // true
2、工廠模式(Factory)
? ? ? ? 定義:用一個函數/類專門負責創建對象,不直接用 new。
? ? ? ? 舉幾個例子,組件封裝:根據類型不同返回不同組件實例還有 Axios 的實例化都是工廠模式。
? ? ? ? 工廠模式的優勢在于能統一創建邏輯,并且拓展非常方便,各類型之間高內聚,低耦合。做個簡單的實現:
function createButton(type) {if (type === 'primary') {return { color: 'blue', size: 'large' };} else if (type === 'danger') {return { color: 'red', size: 'large' };}
}const btn = createButton('primary');
console.log(btn); // { color: 'blue', size: 'large' }
工廠函數是什么??
????????工廠函數(Factory Function)是一個普通函數,它封裝了創建對象的過程,并返回這個對象。它是實現工廠模式的一種方式,但不限于工廠模式使用。
function createPerson(name, age) {return {name,age,sayHello() {console.log(`Hi, I'm ${name}`);}} }const person = createPerson('Tom', 20);
? ? ? ? 這個案例中的 createPerson 就是工廠函數。
3、觀察者模式(Observer)
? ? ? ? 定義:一對多關系,一個對象變化,通知所有依賴它的對象。
? ? ? ??比如Vue 響應式,發布訂閱(EventEmitter)都屬于觀察者模式。
? ? ? ? 一般情況下,觀察者模式里面一定有一個叫做 notify / emit / publish 或類似功能的函數,用來通知所有的訂閱者,這種函數本質上就是廣播機制,是觀察者模式的核心實現函數。做個簡單的案例:維護一組回調,變化時循環觸發。
class Observer {constructor() {this.subscribers = [];}subscribe(fn) {this.subscribers.push(fn);}notify(data) {this.subscribers.forEach(fn => fn(data));}
}const obs = new Observer();obs.subscribe((data) => console.log('收到1', data));
obs.subscribe((data) => console.log('收到2', data));obs.notify('你好');
// 收到1 你好
// 收到2 你好
4、代理模式(Proxy)
? ? ? ? 定義:通過一個代理對象控制對目標對象的訪問。
? ? ? ? Vue3的響應式Proxy、接口請求封裝都屬于代理模式。
? ? ? ? 先用Proxy舉例:
const target = {name: '張三'
};const proxy = new Proxy(target, {get(obj, prop) {console.log(`訪問屬性:${prop}`);return obj[prop];},set(obj, prop, value) {console.log(`設置屬性:${prop}=${value}`);obj[prop] = value;return true;}
});proxy.name; // 訪問屬性:name
proxy.age = 20; // 設置屬性:age=20
? ? ? ? 這個例子比較常見,是不是意猶未盡?我們再來一個圖片懶加載的代理模式來舉例:
// 真正加載圖片的方法
function loadImage(src) {const img = new Image();img.src = src;document.body.appendChild(img);
}// 創建一個代理來控制圖片加載
const proxyImage = (function () {let img = new Image();img.onload = function() {loadImage(this.src); // 真正加載}return function(src) {// 先用一張 loading 圖片占位loadImage('loading.jpg');// 真正的圖片異步加載完成后替換img.src = src;}
})();// 使用
proxyImage('real-image.jpg');
????????proxyImage 函數就是代理對象,loadImage是被代理的目標對象,用戶一調用它就顯示 loading 圖,真正圖片在加載完畢后替換。
????????這里使用 IIFE(立即執行函數表達式) 是為了創建一次性的私有作用域和閉包,隔離 img 變量,避免污染。
????????執行邏輯為:
代碼剛執行時,生成了一個 proxyImage 函數,這個函數內部封裝了一個私有的 Image 對象 img,并且給 img.onload 綁定了回調函數。
調用 proxyImage(src) 時,先 loadImage('loading.jpg') 顯示一個占位圖,再設置 img.src = src(真實圖片地址)。
這時瀏覽器會開始異步加載 src 指向的真實圖片,這一步是靜默執行的,DOM渲染的是默認圖片。
加載完畢后自動觸發 img.onload,在回調里重新調用 loadImage(this.src),替換成真實圖。
? ? ? ? 其中,默認圖片(loading.jpg)應該是一個極小的圖,通過base64編碼內嵌到代碼里,或者直接用svg小圖標,如果是個大一點的圖片應該讓瀏覽器預加載,以此來實現用戶視角下的默認圖片無感渲染。
5、策略模式(Strategy)
? ? ? ? 定義:將一系列算法封裝成獨立的策略類,使它們可以互相替換。
? ? ? ??比如一些表單驗證、支付方式切換過程等。
? ? ? ? 其優勢在于具有開閉原則(增加新策略不用改老代碼),并且代碼可擴展。舉例如下,其中strategies 里是具體的策略集合(算法實現),validate 是「上下文」調用器(根據情況選擇策略執行),兩者配合才構成完整的策略模式實現:
const strategies = {isNonEmpty(value) {return value !== '';},minLength(value, length) {return value.length >= length;}
};function validate(value, rule, ...args) {return strategies[rule](value, ...args);
}console.log(validate('abc', 'minLength', 2)); // true
開閉原則:
????????即對擴展開放,對修改關閉。
? ? ? ? 產生新需求時,應該通過新增代碼來實現,而不是修改現有代碼。這樣可以減少出錯的風險,提高系統的穩定性。
? ? ? ? 在策略模式中,策略模式增加新策略,只是添加新策略對象,不需要去改 validate 函數本體
? ? ? ? 有沒有覺得策略模式和工廠模式很像?都是通過一個“judge info”(type與rule)來判斷到底該如何執行。但他們存在以下區別:
策略模式 | 工廠模式 |
---|---|
關注執行邏輯不同 | 關注對象創建不同 |
替換算法/規則 | 替換對象 |
運行時切換 | 生成時選擇 |
6、建造者模式、適配器模式、裝飾器模式和狀態模式
????????這四個相對少見,簡單介紹:
①建造者模式(Builder Pattern)
????????分步驟構建一個復雜對象,而不是一口氣構造。比如建一臺電腦:
class ComputerBuilder {constructor() {this.computer = {};}addCPU(cpu) {this.computer.cpu = cpu;return this;}addRAM(ram) {this.computer.ram = ram;return this;}addStorage(storage) {this.computer.storage = storage;return this;}build() {return this.computer;}
}const myComputer = new ComputerBuilder().addCPU('Intel i9').addRAM('32GB').addStorage('1TB SSD').build();
②適配器模式(Adapter Pattern)
????????讓原本接口不兼容的兩個類可以一起工作。比如一個老版 API 返回的是 snake_case,但新系統要求 camelCase:
function oldApi() {return { user_name: 'Tom', user_age: 20 };
}// 適配器
function adapterApi() {const data = oldApi();return {userName: data.user_name,userAge: data.user_age};
}const result = adapterApi();
③裝飾器模式(Decorator Pattern)
????????在不修改原對象的情況下,動態增加功能。比如增強 log 函數(這里相當于額外打印了時間):
function log(msg) {console.log(msg);
}// 裝飾器
function withTimestamp(fn) {return function(...args) {console.log(`[${new Date().toISOString()}]`);return fn(...args);}
}const decoratedLog = withTimestamp(log);
decoratedLog('Hello World');
④狀態模式(State Pattern)
????????對象內部狀態不同,行為也不同。比如電燈開關:
class Light {constructor() {this.state = 'off';}toggle() {if (this.state === 'off') {console.log('Turning on');this.state = 'on';} else {console.log('Turning off');this.state = 'off';}}
}const light = new Light();
light.toggle(); // Turning on
light.toggle(); // Turning off
四、結語
????????設計模式能讓人寫出更穩定、可維護的代碼,JavaScript中最常用的設計模式有單例、工廠、觀察者、代理、策略五種。學設計模式就相當于學解決復雜問題的套路,在框架中,如 Vue / React項目里,幾乎隨處可見設計模式的身影。
????????只有鍛煉思維才能可持續地解決問題,只有思維才是真正值得學習和分享的核心要素。如果這篇博客能給您帶來一點幫助,麻煩您點個贊支持一下,還可以收藏起來以備不時之需,有疑問和錯誤歡迎在評論區指出~
????????其他熱門文章,請關注:
? ? ? ??極致的靈活度滿足工程美學:用Vue Flow繪制一個完美流程圖
???? ? ?你真的會使用Vue3的onMounted鉤子函數嗎?Vue3中onMounted的用法詳解
????????DeepSeek:全棧開發者視角下的AI革命者
??? ? ??通過array.filter()實現數組的數據篩選、數據清洗和鏈式調用
??? ? ??通過Array.sort() 實現多字段排序、排序穩定性、隨機排序洗牌算法、優化排序性能
??? ? ??TreeSize:免費的磁盤清理與管理神器,解決C盤爆滿的燃眉之急
??? ? ??通過MongoDB Atlas 實現語義搜索與 RAG——邁向AI的搜索機制
???? ? ?深入理解 JavaScript 中的 Array.find() 方法:原理、性能優勢與實用案例詳解
???? ? ?el-table實現動態數據的實時排序,一篇文章講清楚elementui的表格排序功能
??? ? ??MutationObserver詳解+案例——深入理解 JavaScript 中的 MutationObserver
????????JavaScript中通過array.map()實現數據轉換、創建派生數組、異步數據流處理、DOM操作等
????????前端實戰:基于Vue3與免費滿血版DeepSeek實現無限滾動+懶加載+瀑布流模塊及優化策略
????????高效工作流:用Mermaid繪制你的專屬流程圖;如何在Vue3中導入mermaid繪制流程圖
????????干貨含源碼!如何用Java后端操作Docker(命令行篇)
????????在線編程實現!如何在Java后端通過DockerClient操作Docker生成python環境
??? ? ??Dockerfile全面指南:從基礎到進階,掌握容器化構建的核心工具