探索 JS 中五大常用設(shè)計模式,再也別說設(shè)計模式?jīng)]用啦!
今天,咱們來看一下 JS 中的常用設(shè)計模式。主要有 5 種:
- 單例模式
- 觀察者模式
- 工廠模式
- 模塊模式
- 裝飾器模式
一、單例模式
單例模式是一種設(shè)計模式,它確保一個類只有一個實例,并提供一個全局訪問點來獲取這個實例。
在 JavaScript 中,單例模式通常用于 創(chuàng)建唯一的對象實例,例如全局配置、共享資源或狀態(tài)管理器。這樣可以避免重復(fù)創(chuàng)建實例,節(jié)省資源,并確保全局?jǐn)?shù)據(jù)的一致性。
在前端框架中的應(yīng)用
- Vue.js: 在 Vue 中,單例模式通常用來創(chuàng)建 Vuex store(狀態(tài)管理器),它確保整個應(yīng)用程序只有一個 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 中,類似的單例模式可以用于創(chuàng)建全局狀態(tài)管理庫,如 Redux 或者 React Context API。Redux 的 store 也是一個單例,確保整個應(yīng)用使用的是同一個狀態(tài)樹。
// 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;
單例模式的基本實現(xiàn)
通過閉包來實現(xiàn)單例模式:
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
適用場景
- 全局配置: 當(dāng)你需要一個全局唯一的配置對象,供整個應(yīng)用程序使用時,比如應(yīng)用的配置設(shè)置、日志系統(tǒng)等。
- 狀態(tài)管理: 當(dāng)你需要一個全局的狀態(tài)管理工具(如 Vuex 或 Redux)來管理應(yīng)用程序的狀態(tài),確保狀態(tài)的一致性和集中管理。
二、觀察者模式
觀察者模式用于定義對象之間的一對多依賴關(guān)系。
當(dāng)一個對象的狀態(tài)發(fā)生變化時,所有依賴于它的對象都會得到通知并自動更新。這個模式通常用于實現(xiàn)發(fā)布-訂閱(pub-sub)機制。
主要優(yōu)點在于它提供了一個解耦的方式來管理對象之間的依賴關(guān)系。
在前端框架中的應(yīng)用
- Vue.js: Vue2 的響應(yīng)式系統(tǒng)就是基于觀察者模式的。當(dāng) Vue 組件的依賴數(shù)據(jù)發(fā)生變化時,相關(guān)組件會被重新渲染。
// Vue 組件示例
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
- React: React 使用了類似于觀察者模式的機制來實現(xiàn)組件的重新渲染。當(dāng)組件的狀態(tài)(state)或?qū)傩裕╬rops)發(fā)生變化時,React 會重新渲染相關(guān)的組件。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { message: 'Hello React!' };
}
render() {
return <div>{this.state.message}</div>;
}
}
觀察者模式的基本實現(xiàn)
通過事件監(jiān)聽機制來實現(xiàn):
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(`數(shù)據(jù)為: ${data}`);
}
}
適用場景
- 事件處理: 當(dāng)需要處理用戶交互、系統(tǒng)事件或其他異步事件時。例如,監(jiān)聽按鈕點擊事件或處理服務(wù)器推送的數(shù)據(jù)。
- 數(shù)據(jù)綁定: 在構(gòu)建數(shù)據(jù)驅(qū)動的用戶界面時(類似 vue),例如:表單數(shù)據(jù)和視圖之間的雙向綁定,觀察者模式可以幫助自動更新視圖。
- 發(fā)布-訂閱系統(tǒng): 當(dāng)需要構(gòu)建一個松耦合的系統(tǒng),其中多個組件可以訂閱和發(fā)布事件時,觀察者模式是一個很好的選擇。例如,實現(xiàn)聊天系統(tǒng)中的消息通知功能。
三、工廠模式
工廠模式是一種創(chuàng)建型設(shè)計模式,用于定義一個創(chuàng)建對象的接口,但讓子類決定實例化哪個類。
工廠模式通過將對象的創(chuàng)建過程封裝到一個工廠類中,使得代碼可以在不暴露對象創(chuàng)建邏輯的情況下使用這些對象。主要優(yōu)點在于對象的創(chuàng)建和使用之間的耦合度降低。
在前端框架中的應(yīng)用
- Vue.js: Vue 的組件系統(tǒng)可以被視為工廠模式的一種實現(xiàn)。在 Vue 中,你可以使用工廠函數(shù)來創(chuàng)建不同的組件實例。例如,Vue 的 Vue.component 方法實際上就是一個工廠方法,它用來注冊和創(chuàng)建組件。
// 注冊一個全局組件
Vue.component('my-component', {
template: '<div>Hello World</div>'
});
// 創(chuàng)建組件實例
new Vue({
el: '#app',
template: '<my-component></my-component>'
});
- React: React 的組件創(chuàng)建和管理也是工廠模式的一種應(yīng)用。你可以定義組件類或函數(shù)組件,然后 React 會根據(jù)需要創(chuàng)建和管理這些組件的實例。
function MyComponent() {
return <div>Hello World</div>;
}
// 使用組件
ReactDOM.render(<MyComponent />, document.getElementById('root'));
工廠模式的基本實現(xiàn)
工廠模式可以通過函數(shù)或類來實現(xiàn):
// 工廠函數(shù)
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' }
適用場景
- 對象創(chuàng)建邏輯復(fù)雜: 當(dāng)對象的創(chuàng)建過程復(fù)雜,涉及到多個步驟或配置時,工廠模式可以將這些細(xì)節(jié)封裝起來。
- 對象實例化的變體: 當(dāng)需要創(chuàng)建不同類型的對象,但對象的創(chuàng)建過程基本相似時,可以使用工廠模式來簡化創(chuàng)建過程。例如,根據(jù)不同的配置創(chuàng)建不同類型的組件或模塊。
- 依賴注入: 當(dāng)需要在創(chuàng)建對象時注入不同的依賴項或配置時,工廠模式可以幫助管理這些依賴項并確保對象的正確初始化。
- 解耦: 當(dāng)需要將對象的創(chuàng)建和使用解耦時,工廠模式可以提供一個統(tǒng)一的創(chuàng)建接口,使得客戶端代碼不需要關(guān)心對象的具體創(chuàng)建過程。
四、模塊模式
模塊模式用于創(chuàng)建一個具有私有和公有方法的模塊,并封裝模塊的內(nèi)部狀態(tài)。這種模式在前端的框架或者項目中是使用最廣的。
在前端框架中的應(yīng)用
- 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 中,組件也是一種模塊化的實踐。每個組件通常都放在一個單獨的文件中,這樣可以封裝組件的狀態(tài)和邏輯。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;
模塊模式的基本實現(xiàn)
在 JavaScript 中,模塊模式通常通過立即調(diào)用的函數(shù)表達(dá)式(IIFE)來實現(xiàn)。這種模式能夠封裝模塊的私有變量和方法,同時暴露一些公有接口給外部使用。
const myModule = (function() {
// 私有變量和方法
let privateVar = '我是私有的';
function privateMethod() {
console.log(privateVar);
}
// 公有變量和方法
return {
publicVar: '我是公有的',
publicMethod: function() {
privateMethod();
}
};
})();
適用場景
- 封裝和組織代碼: 當(dāng)你需要封裝內(nèi)部邏輯,避免全局命名沖突,并將功能模塊化時,可以使用模塊模式。
- 私有和公有方法: 當(dāng)需要隱藏模塊的私有方法和變量,僅暴露必要的公有接口時,模塊模式提供了一種清晰的方式來實現(xiàn)這一點。
- 代碼復(fù)用: 當(dāng)你希望將功能或組件拆分成獨立的模塊,以便在不同的地方復(fù)用時,模塊模式可以幫助你更好地組織和管理這些模塊。
五、裝飾器模式
裝飾器模式是一種結(jié)構(gòu)型設(shè)計模式,用于動態(tài)地為對象添加額外的功能,而不修改其結(jié)構(gòu)。它通常會通過創(chuàng)建一個裝飾器對象來包裝原始對象,并在裝飾器對象中添加或修改功能。
在前端框架中的應(yīng)用
在 Vue 或者 react 中,雖然沒有直接使用裝飾器模式,但有類似的概念,如 Vue 的 mixins 和 React 的高階組件。
- Vue.js: Vue 的 mixins 功能可以看作是一種裝飾器模式的實現(xiàn)。Mixins 允許你將可復(fù)用的功能代碼抽象成一個對象,并將其混入到 Vue 組件中,以便共享和復(fù)用。
// 定義一個 mixin
const myMixin = {
data() {
return {
mixinData: '我是 mixin 的數(shù)據(jù)'
};
},
methods: {
mixinMethod() {
console.log('我是 mixin 的方法');
}
}
};
// 使用 mixin
new Vue({
el: '#app',
mixins: [myMixin],
created() {
this.mixinMethod(); // '我是 mixin 的數(shù)據(jù)'
}
});
- React: 在 React 中,裝飾器模式也不是直接使用的,但高階組件(HOC)可以看作是一種裝飾器模式的應(yīng)用。HOC 是一個函數(shù),接受一個組件并返回一個新的組件,該新組件增加了額外的功能或數(shù)據(jù)。
// 高階組件
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'));
裝飾器模式的基本實現(xiàn)
裝飾器模式可以通過高階函數(shù)或類裝飾器來實現(xiàn):
// 原始對象
class Coffee {
cost() {
return 5;
}
}
// 裝飾器函數(shù)
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
適用場景
- 動態(tài)擴展功能: 當(dāng)你需要在運行時動態(tài)地為對象添加功能,而不修改對象的結(jié)構(gòu)時,裝飾器模式提供了一種靈活的方式來實現(xiàn)這一點。
- 組合功能: 當(dāng)你需要組合多個裝飾器來創(chuàng)建一個具有多個功能的對象時,裝飾器模式提供了一種清晰的方式來實現(xiàn)這一點。