探索 JS 中五大常用設計模式,再也別說設計模式沒用啦!
今天,咱們來看一下 JS 中的常用設計模式。主要有 5 種:
- 單例模式
- 觀察者模式
- 工廠模式
- 模塊模式
- 裝飾器模式
一、單例模式
單例模式是一種設計模式,它確保一個類只有一個實例,并提供一個全局訪問點來獲取這個實例。
在 JavaScript 中,單例模式通常用于 創建唯一的對象實例,例如全局配置、共享資源或狀態管理器。這樣可以避免重復創建實例,節省資源,并確保全局數據的一致性。
在前端框架中的應用
- Vue.js: 在 Vue 中,單例模式通常用來創建 Vuex store(狀態管理器),它確保整個應用程序只有一個 store 實例
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
});
export default store;
- React: 在 React 中,類似的單例模式可以用于創建全局狀態管理庫,如 Redux 或者 React Context API。Redux 的 store 也是一個單例,確保整個應用使用的是同一個狀態樹。
// store.js
import { createStore } from 'redux';
const initialState = { count: 0 };
function reducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
}
const store = createStore(reducer);
export default store;
單例模式的基本實現
通過閉包來實現單例模式:
const Singleton = (function () {
let instance;
function createInstance() {
return { name: "張三" };
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// 使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
適用場景
- 全局配置: 當你需要一個全局唯一的配置對象,供整個應用程序使用時,比如應用的配置設置、日志系統等。
- 狀態管理: 當你需要一個全局的狀態管理工具(如 Vuex 或 Redux)來管理應用程序的狀態,確保狀態的一致性和集中管理。
二、觀察者模式
觀察者模式用于定義對象之間的一對多依賴關系。
當一個對象的狀態發生變化時,所有依賴于它的對象都會得到通知并自動更新。這個模式通常用于實現發布-訂閱(pub-sub)機制。
主要優點在于它提供了一個解耦的方式來管理對象之間的依賴關系。
在前端框架中的應用
- Vue.js: Vue2 的響應式系統就是基于觀察者模式的。當 Vue 組件的依賴數據發生變化時,相關組件會被重新渲染。
// Vue 組件示例
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
- React: React 使用了類似于觀察者模式的機制來實現組件的重新渲染。當組件的狀態(state)或屬性(props)發生變化時,React 會重新渲染相關的組件。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { message: 'Hello React!' };
}
render() {
return <div>{this.state.message}</div>;
}
}
觀察者模式的基本實現
通過事件監聽機制來實現:
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notifyObservers(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log(`數據為: ${data}`);
}
}
適用場景
- 事件處理: 當需要處理用戶交互、系統事件或其他異步事件時。例如,監聽按鈕點擊事件或處理服務器推送的數據。
- 數據綁定: 在構建數據驅動的用戶界面時(類似 vue),例如:表單數據和視圖之間的雙向綁定,觀察者模式可以幫助自動更新視圖。
- 發布-訂閱系統: 當需要構建一個松耦合的系統,其中多個組件可以訂閱和發布事件時,觀察者模式是一個很好的選擇。例如,實現聊天系統中的消息通知功能。
三、工廠模式
工廠模式是一種創建型設計模式,用于定義一個創建對象的接口,但讓子類決定實例化哪個類。
工廠模式通過將對象的創建過程封裝到一個工廠類中,使得代碼可以在不暴露對象創建邏輯的情況下使用這些對象。主要優點在于對象的創建和使用之間的耦合度降低。
在前端框架中的應用
- Vue.js: Vue 的組件系統可以被視為工廠模式的一種實現。在 Vue 中,你可以使用工廠函數來創建不同的組件實例。例如,Vue 的 Vue.component 方法實際上就是一個工廠方法,它用來注冊和創建組件。
// 注冊一個全局組件
Vue.component('my-component', {
template: '<div>Hello World</div>'
});
// 創建組件實例
new Vue({
el: '#app',
template: '<my-component></my-component>'
});
- React: React 的組件創建和管理也是工廠模式的一種應用。你可以定義組件類或函數組件,然后 React 會根據需要創建和管理這些組件的實例。
function MyComponent() {
return <div>Hello World</div>;
}
// 使用組件
ReactDOM.render(<MyComponent />, document.getElementById('root'));
工廠模式的基本實現
工廠模式可以通過函數或類來實現:
// 工廠函數
function Car(make, model) {
this.make = make;
this.model = model;
}
function createCar(make, model) {
return new Car(make, model);
}
// 使用示例
const car1 = createCar('理想', 'L9');
const car2 = createCar('問界', 'M9');
console.log(car1); // Car { make: '理想', model: 'L9' }
console.log(car2); // Car { make: '問界', model: 'M9' }
適用場景
- 對象創建邏輯復雜: 當對象的創建過程復雜,涉及到多個步驟或配置時,工廠模式可以將這些細節封裝起來。
- 對象實例化的變體: 當需要創建不同類型的對象,但對象的創建過程基本相似時,可以使用工廠模式來簡化創建過程。例如,根據不同的配置創建不同類型的組件或模塊。
- 依賴注入: 當需要在創建對象時注入不同的依賴項或配置時,工廠模式可以幫助管理這些依賴項并確保對象的正確初始化。
- 解耦: 當需要將對象的創建和使用解耦時,工廠模式可以提供一個統一的創建接口,使得客戶端代碼不需要關心對象的具體創建過程。
四、模塊模式
模塊模式用于創建一個具有私有和公有方法的模塊,并封裝模塊的內部狀態。這種模式在前端的框架或者項目中是使用最廣的。
在前端框架中的應用
- Vue.js: Vue.js 中的單文件組件(.vue 文件)實際上就是模塊化的方案,它允許將模板、腳本和樣式封裝在一個文件中,并通過 export 和 import 語法來管理這些模塊
<!-- MyComponent.vue -->
<template>
<div>{{message}}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
};
}
};
</script>
<style>
div {
color: red;
}
</style>
- React: 在 React 中,組件也是一種模塊化的實踐。每個組件通常都放在一個單獨的文件中,這樣可以封裝組件的狀態和邏輯。React 使用 ES6 的 import 和 export 來管理組件模塊。
// MyComponent.js
import React from 'react';
function MyComponent() {
return <div>Hello, React!</div>;
}
export default MyComponent;
// App.js
import React from 'react';
// 模塊化
import MyComponent from './MyComponent';
function App() {
return (
<div>
<MyComponent />
</div>
);
}
export default App;
模塊模式的基本實現
在 JavaScript 中,模塊模式通常通過立即調用的函數表達式(IIFE)來實現。這種模式能夠封裝模塊的私有變量和方法,同時暴露一些公有接口給外部使用。
const myModule = (function() {
// 私有變量和方法
let privateVar = '我是私有的';
function privateMethod() {
console.log(privateVar);
}
// 公有變量和方法
return {
publicVar: '我是公有的',
publicMethod: function() {
privateMethod();
}
};
})();
適用場景
- 封裝和組織代碼: 當你需要封裝內部邏輯,避免全局命名沖突,并將功能模塊化時,可以使用模塊模式。
- 私有和公有方法: 當需要隱藏模塊的私有方法和變量,僅暴露必要的公有接口時,模塊模式提供了一種清晰的方式來實現這一點。
- 代碼復用: 當你希望將功能或組件拆分成獨立的模塊,以便在不同的地方復用時,模塊模式可以幫助你更好地組織和管理這些模塊。
五、裝飾器模式
裝飾器模式是一種結構型設計模式,用于動態地為對象添加額外的功能,而不修改其結構。它通常會通過創建一個裝飾器對象來包裝原始對象,并在裝飾器對象中添加或修改功能。
在前端框架中的應用
在 Vue 或者 react 中,雖然沒有直接使用裝飾器模式,但有類似的概念,如 Vue 的 mixins 和 React 的高階組件。
- Vue.js: Vue 的 mixins 功能可以看作是一種裝飾器模式的實現。Mixins 允許你將可復用的功能代碼抽象成一個對象,并將其混入到 Vue 組件中,以便共享和復用。
// 定義一個 mixin
const myMixin = {
data() {
return {
mixinData: '我是 mixin 的數據'
};
},
methods: {
mixinMethod() {
console.log('我是 mixin 的方法');
}
}
};
// 使用 mixin
new Vue({
el: '#app',
mixins: [myMixin],
created() {
this.mixinMethod(); // '我是 mixin 的數據'
}
});
- React: 在 React 中,裝飾器模式也不是直接使用的,但高階組件(HOC)可以看作是一種裝飾器模式的應用。HOC 是一個函數,接受一個組件并返回一個新的組件,該新組件增加了額外的功能或數據。
// 高階組件
function withExtraProps(WrappedComponent) {
return function EnhancedComponent(props) {
return <WrappedComponent {...props} extraProp="extra" />;
};
}
// 原始組件
function MyComponent(props) {
return <div>{props.extraProp}</div>;
}
// 使用高階組件
const EnhancedComponent = withExtraProps(MyComponent);
ReactDOM.render(<EnhancedComponent />, document.getElementById('root'));
裝飾器模式的基本實現
裝飾器模式可以通過高階函數或類裝飾器來實現:
// 原始對象
class Coffee {
cost() {
return 5;
}
}
// 裝飾器函數
function MilkDecorator(coffee) {
const originalCost = coffee.cost();
coffee.cost = function() {
return originalCost + 2;
};
return coffee;
}
// 使用示例
const myCoffee = new Coffee();
console.log(myCoffee.cost()); // 5
const myMilkCoffee = MilkDecorator(myCoffee);
console.log(myMilkCoffee.cost()); // 7
適用場景
- 動態擴展功能: 當你需要在運行時動態地為對象添加功能,而不修改對象的結構時,裝飾器模式提供了一種靈活的方式來實現這一點。
- 組合功能: 當你需要組合多個裝飾器來創建一個具有多個功能的對象時,裝飾器模式提供了一種清晰的方式來實現這一點。