前端常用设计模式与场景
单例、观察者、策略、代理等设计模式在前端的实现与应用
问题
前端开发中常用哪些设计模式?各自的使用场景是什么?
解答
1. 单例模式
确保一个类只有一个实例。
// 单例模式
class Store {
static instance = null
constructor() {
if (Store.instance) {
return Store.instance
}
this.state = {}
Store.instance = this
}
setState(key, value) {
this.state[key] = value
}
getState(key) {
return this.state[key]
}
}
// 使用
const store1 = new Store()
const store2 = new Store()
console.log(store1 === store2) // true
场景:全局状态管理、弹窗组件、登录框
2. 发布订阅模式
对象间一对多的依赖关系,状态变化时通知所有订阅者。
class EventEmitter {
constructor() {
this.events = {}
}
// 订阅
on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
return this
}
// 发布
emit(event, ...args) {
const callbacks = this.events[event]
if (callbacks) {
callbacks.forEach(cb => cb(...args))
}
return this
}
// 取消订阅
off(event, callback) {
const callbacks = this.events[event]
if (callbacks) {
this.events[event] = callbacks.filter(cb => cb !== callback)
}
return this
}
// 只订阅一次
once(event, callback) {
const wrapper = (...args) => {
callback(...args)
this.off(event, wrapper)
}
this.on(event, wrapper)
return this
}
}
// 使用
const emitter = new EventEmitter()
emitter.on('login', user => console.log(`${user} 登录了`))
emitter.emit('login', '张三') // 张三 登录了
场景:组件通信、事件总线、Vue 的 $emit
3. 策略模式
定义一系列算法,把它们封装起来,使它们可以互相替换。
// 表单验证策略
const strategies = {
required(value, message) {
return value.trim() === '' ? message : ''
},
minLength(value, length, message) {
return value.length < length ? message : ''
},
mobile(value, message) {
return !/^1[3-9]\d{9}$/.test(value) ? message : ''
},
email(value, message) {
return !/^\w+@\w+\.\w+$/.test(value) ? message : ''
}
}
// 验证器
class Validator {
constructor() {
this.rules = []
}
add(value, rule, ...args) {
this.rules.push(() => strategies[rule](value, ...args))
return this
}
validate() {
for (const rule of this.rules) {
const error = rule()
if (error) return error
}
return ''
}
}
// 使用
const validator = new Validator()
validator
.add('', 'required', '用户名不能为空')
.add('abc', 'minLength', 6, '用户名至少6位')
console.log(validator.validate()) // 用户名不能为空
场景:表单验证、支付方式选择、不同权限的操作
4. 代理模式
为对象提供一个代理,控制对原对象的访问。
// 图片懒加载代理
const lazyLoadImage = (function() {
const img = document.createElement('img')
return {
setSrc(node, src) {
// 先显示占位图
node.src = 'loading.gif'
// 加载真实图片
img.onload = () => {
node.src = src
}
img.src = src
}
}
})()
// 缓存代理
const memoize = (fn) => {
const cache = new Map()
return function(...args) {
const key = JSON.stringify(args)
if (cache.has(key)) {
return cache.get(key)
}
const result = fn.apply(this, args)
cache.set(key, result)
return result
}
}
// 使用
const fibonacci = memoize(n => {
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
})
console.log(fibonacci(40)) // 快速计算
场景:图片懒加载、缓存计算结果、Vue3 响应式(Proxy)
5. 装饰器模式
动态地给对象添加额外的职责。
// 函数装饰器 - 添加日志
function withLog(fn) {
return function(...args) {
console.log(`调用 ${fn.name},参数:`, args)
const result = fn.apply(this, args)
console.log(`返回值:`, result)
return result
}
}
// 函数装饰器 - 防抖
function debounce(fn, delay) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
// 使用
const add = (a, b) => a + b
const loggedAdd = withLog(add)
loggedAdd(1, 2)
// 调用 add,参数: [1, 2]
// 返回值: 3
// React HOC 也是装饰器模式
function withAuth(Component) {
return function AuthComponent(props) {
const isLogin = checkLogin()
if (!isLogin) {
return <Login />
}
return <Component {...props} />
}
}
场景:React HOC、防抖节流、日志记录、权限校验
6. 工厂模式
封装创建对象的逻辑,不暴露具体实现。
// 简单工厂
class Button {
constructor(type) {
this.type = type
}
render() {
return `<button class="${this.type}">按钮</button>`
}
}
class Input {
constructor(type) {
this.type = type
}
render() {
return `<input type="${this.type}" />`
}
}
// 工厂函数
function createComponent(type, config) {
switch(type) {
case 'button':
return new Button(config.style)
case 'input':
return new Input(config.inputType)
default:
throw new Error('未知组件类型')
}
}
// 使用
const btn = createComponent('button', { style: 'primary' })
const input = createComponent('input', { inputType: 'text' })
场景:创建组件实例、根据配置生成不同对象
关键点
- 单例模式:全局唯一实例,适合状态管理、弹窗
- 发布订阅:解耦组件通信,是事件系统的基础
- 策略模式:消除 if-else,算法可替换,适合表单验证
- 代理模式:控制访问,适合懒加载、缓存、Vue3 响应式
- 装饰器模式:不修改原函数增强功能,适合 HOC、防抖节流
- 工厂模式:封装创建逻辑,适合根据配置创建不同实例
目录