js常见的设计模式有哪些?使用场景是什么?
设计模式是针对软件使用场景的解决方案,也可理解为特定问题的处理方案。
常见的设计模式
- 单例模式
- 代理模式
- 策略模式
- 迭代器模式
- 发布订阅模式
- 工厂模式
- 观察者模式
单例模式
单例模式特点就是全局只能有一个实例,重新创建的话,如果已经创建,则会将之前创建的实例返回。
使用场景
- 全局消息提示
- Vue项目,全局只有一个项目实例
- 网站登录入口
- 背景音频只有一个audio组件
- 全局状态存储,localStorage、sessionStorage、cookie
简单实现
ts
function createTarget() {
class Person {
constructor(name) {
this.name = name
}
}
let child = null
return function (name) {
if (!child)
child = new Person(name)
return child
}
}
代理模式
代理模式,是指通过一个类A控制,另外一个类B的功能,不能直接访问B,又称为结构型模式。
在代理模式中,只能创建A的实例,访问B的功能。
特点
- 使用者无权访问目标对象
- 中间代理,通过代理做授权控制
使用场景
- 事件委托,将事件绑定到ol、ul上实现li事件的代理
- Vue3响应式系统,通过Proxy对象代理响应式对象,实现数据的操作变化控制
简单实现
ts
// 该类型不对外暴露
class Person {
constructor(name) {
this.name = name
}
say() {
console.log(this.name)
}
}
// 对外暴露类型, 对外暴露的类可能不止这一个受控类
class Student {
constructor(name) {
this.name = name
this.person = new Person(name)
}
say() {
this.person.say()
}
}
const child = new Student('小明')
child.say() // 小明
策略模式
根据场景的不同进行封装,可以根据不同的环境场景进行动态切换。
特点
- 条件判断
- 功能兼容
使用场景
- 项目中的条件判断if、else、Switch case等
- 项目构建常用的环境变量env/env.production/env.development
简单实现
ts
const target = {
A: 1,
B: 2,
C: 3,
}
function foo(key) {
return target[key]
}
迭代器模式
迭代器模式提供一个方法,能够顺序访问每一个聚合对象的元素
特点
- 访问一个聚合对象的内容而无需暴露它的内部构成。
- 为遍历不同的集合提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
- 迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
使用场景
- 任意需要遍历循环的功能
简单实现
ts
function _each(arr, fn) {
for (let i = 0; i < arr.length; i++)
fn(i, arr[i])
}
发布订阅模式
发布订阅模式定义了一对多的关系,让多个订阅者同时监听某一个监听对象,当监听对象变化时,他会通知所有的订阅者。
特点
- 实现发布者和订阅者之间的解耦关系,提高了代码的维护性和复用性
- 支持异步处理,实现了延迟处理和批量处理
- 如果订阅者不及时取消订阅则会造成内存泄漏
使用场景
- vue数据响应式系统
- 核心在于多个功能模块需要依赖该对象,通过该对象的变化进行切换
- eventBus
简单实现
ts
// 对象模式
const Mitt = {
// 用于缓存
list: {},
// 订阅,并缓存回调函数
subscribe(name, fn) {
const item = this.list[name]
if (item && Array.isArray(item) && item.length)
item.push(fn)
else
this.list[name] = []
},
// 取消订阅,如果取消订阅,不传方法则默认全部清空
unsubscribe(name, fn) {
const item = this.list[name]
if (!item)
return
if (!fn)
delete this.list[name]
const index = item.findIndex(f => f === fn)
if (index > -1)
item.splice(index, 1)
},
publish(name, params) {
const item = this.list[name]
if (!item || !item.length)
return
item.forEach((fn) => {
fn(params)
})
}
}
// 字典模式
class Mitt {
constructor() {
this.map = new Map()
}
emit(name, params) {
const item = this.map.get(name)
if (!item)
return
item.forEach((fn) => {
fn(params)
})
}
on(name, fn) {
const item = this.map.get(name)
if (item && item.length)
item.push(fn)
else
this.map.set(name, [fn])
}
off(name, fn) {
const item = this.map.get(name)
if (!item)
return
if (!fn)
this.map.set(name, [])
const index = item.findIndex(f => f === fn)
if (index > -1)
item.splice(index, 1)
}
clear() {
this.map.clear()
}
}
工厂模式
工厂模式是指根据不同的要求,生产出不同的实例对象。
特点
- 统一管理不同的功能,按照需求返回
- 封装简单实现最少、最简单的值,返回不同的实例
使用场景
- vue-router
- vue
- 处理大量相同属性的小对象
简单实现
ts
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
info() {
console.log(this.name, this.age)
}
}
class UserFactory {
constructor() {}
create(person) {
switch (person) {
case 'old':
return new Person('老人', 80)
break
case 'child':
return new Person('小孩', 10)
break
default:
throw new Error('参数错误')
}
}
}
观察者模式
观察者模式 :定义一些一对多的关系,将一系列的观察者对目标函数感兴趣,将自己添加进目标函数,当目标函数状态发生改变时,发送通知,以此通知附加在目标上的观察者
使用场景
- vue的watch
- dom的mutationObserver
简单实现
ts
class Subject {
constructor() {
this.handlers = []
}
add(fn) {
this.handlers.push(fn)
}
notify() {
this.handlers.forEach(fn => fn.update())
}
}
class Observer {
constructor(name) {
this.name = name
}
update() {
console.log(this.name)
}
}
const subject = new Subject()
subject.add(new Observer('1111111'))
subject.notify() // 111111