# js设计模式
# 构造器模式 和 原型模式
构造器模式用于创建具有相同属性和方法的多个实例,可以提高代码的复用性。 原型模式用于创建具有相同方法的多个实例,该放法挂在到实例的原型上。
# es6写法
class Person {
constructor(name, age) { //构造器
this.name = name;
this.age = age;
}
//将方法挂载到原型上
sayHello() {
console.log(`hello, my name is ${this.name}`);
}
}
let p1 = new Person('张三', 18);
let p2 = new Person('李四', 20);
console.log(p1.name); // 张三
console.log(p2.name); // 李四
# 工厂模式
由一个工厂函数来创建对象,工厂函数根据传入的参数不同,返回不同类型的对象。
class User {
constructor(name, pages) {
this.rote = name;//人员
this.pages = pages;//权限
}
//静态方法 不用new也能获取到
static userFactory = (rote) => {
switch (rote) {
case 'admin':
return new User('admin', 1000);
break;
case 'user':
return new User('user', 100);
break;
default:
return new User('guest', 10);
break;
}
}
}
console.log(User.userFactory('admin'))//User { rote: 'admin', pages: 1000 }
console.log(User.userFactory('user'))//User { rote: 'user', pages: 100 }
# 抽象工厂模式
抽象工厂模式是工厂模式的升级版,工厂函数返回的是一个工厂对象,工厂对象根据传入的参数不同,返回不同类型的对象。
抽象工厂模式通常是由多个子类继承父类的形式实现,子类根据不同的参数返回不同的对象。
class People {
constructor(name, age) {
this.name = name;
this.age = age;
}
static sayHello() {
console.log(`hello, my name is ${this.name}`);
}
//定义抽象方法 抽象方法必须被子类重写
hobby() {
throw new Error('抽象方法不能被调用')
}
}
const p1 = new People('张三', 18);
class lisa extends People {
hobby() {
console.log('看书');
}
}
const lisa1 = new lisa('李四', 20);
lisa1.hobby()
console.log(lisa1)
# 建造者模式
建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
class NavBar {
init() {
console.log('NavBar初始化')
}
getData() {
console.log('NavBar获取数据')
}
render() {
console.log('NavBar渲染')
}
}
class List {
init() {
console.log('List初始化')
}
getData() {
return new Promise((res) => {
setTimeout(() => {
res('List获取数据')
}, 2000)
})
}
render() {
console.log('List渲染')
}
}
//创建建造者
class Builder {
async startBuild(builder) {
builder.init()
await builder.getData()
builder.render()
}
}
const op = new Builder()
op.startBuild(new NavBar())
op.startBuild(new List())
# 单例模式
单列模式保证一个类只有一个实例
主要解决一个全局的类频繁的创建和销毁
当该类已经创建过时返回上次创建的实例
实现方式:利用闭包实现单例
class People {
constructor(name, age) {
if (!People.instance) {
this.name = name;
this.age = age;
People.instance = this;
}
return People.instance;
}
}
console.log('1', new People('张三', 18))//People { name: '张三', age: 18 }
console.log('2', new People('李四', 20))//People { name: '张三', age: 18 }
console.log('3', new People('王五', 40))//People { name: '张三', age: 18 }
# 装饰器模式
- 装饰器模式能够很好的对已有的功能进行拓展,这样不会更改已有的代码,对其他业务产生影响这方便我们在较少的改动下对功能进行拓展
- 在函数的前后添加一些前置或后置的函数可以在该函数执行之前或之后执行一些代码
- 前置和后置函数通常使用
before和after关键字来区分
//函数原型添加before 前置方法
Function.prototype.before = function (beforeFn) {
//存储当前函数
const _tistFn = this
return function (...args) {
//调用前置函数
beforeFn.apply(this, args)
return _tistFn.apply(this, args)////调用函数后将函数的返回值返回
}
}
const text = () => {
console.log('hellow')
return 111
}
//装饰器模式
//装饰器模式前置函数在运行时会先运行前置函数
const newText = text.before(() => {
console.log('before')
})
console.log(newText())//before hellow 111
# 适配器模式
- 适配器模式将一个类的接口转换成另一个类的接口,适配器模式使得类间存在直接的依赖关系,而不是通过继承或组合来实现。
- 使得不同的代码能够兼容调用
class tenxun {
constructor(name, sex) {
this.name = name;
this.sex = sex
}
show() {
console.log('开始渲染腾讯地图')
}
}
class baidu {
display() {
console.log('开始渲染百度地图')
}
}
//使用适配器模式 整合调用方法
class newTenxun extends tenxun {
constructor(name, age, sex) {
super(name, sex);//super关键字表示父类的构造函数
this.age = age;
}
display() {
super.show()
}
}
//使用适配器模式整合调用方法
//统一使用display方法调用
const rander = (map) => {
map.display()
}
console.log(new tenxun('张三', '男'))
console.log(new newTenxun('李四', '18', '男'))
rander(new newTenxun())
rander(new baidu())
# 策略模式
- 策略模式定义了算法和算法的执行策略,使得算法可以独立于算法的执行策略而变化
- 相当于
map映射
const map = {
1: () => console.log('审核中'),
2: () => console.log('审核通过'),
3: () => console.log('审核不通过')
}
map[1]()//审核中
map[2]()//审核通过
# 代理模式
- 代理模式为一个类提供一个代理对象,这个代理对象可以实现对被代理对象的访问和操作
- 代理模式可以理解为一个中介,它可以将客户端的请求转发给真正的目标对象,从而达到控制目标对象的目的
class start {
constructor(name) {
this.name = name
}
wark() {
console.log(this.name, '开始工作')
}
}
//设置代理
class proxy {
constructor() {
this.newStart = new start('张三')
}
talk(price) {
if (price >= 1000) {
this.newStart.wark()
} else {
console.log('不工作')
}
}
}
new proxy().talk(10)
new proxy().talk(1000)
# es6中的proxy代理对象
proxy代理对象提供了get和set方法,可以对属性的访问进行拦截
const star = {
name: '张三',
age: 18,
}
const proxy = new Proxy(star, {
get(target, key) {
//target 当前代理的对象
//key 当前访问代理对象的属性名
console.log('当前访问的属性名', key)
return target[key]
},
set(target, key, value) {
//target 当前代理的对象
//key 当前访问代理对象的属性名
//value 当前代理对象的属性值
console.log('当前设置的属性名', key)
console.log('当前设置的属性值', value)
target[key] = value
}
})
console.log(proxy.name)//当前访问的属性名 name
proxy.name = '李四'
console.log(proxy.name)
//原始对象改变
console.log(star)//{ name: '李四', age: 18 }
注意
使用proxy代理更改对象时该代理对象的原始数据也会发生更改
# 观察者模式
- 观察者模式包括观察者和观察目标两类对象
//观察目标
class star {
constructor() {
this.oberver = []
}
//添加观察者
add(oberver) {
this.oberver.push(oberver)
}
//移除观察者
remove(oberver) {
this.oberver = this.oberver.filter(item => item !== oberver)
}
//观察者模式发送通知
notify() {
this.oberver.forEach(item => {
item.update()
})
}
}
//观察者
class oberver {
constructor(name) {
this.name = name
}
update() {
console.log(this.name, '更新了')
}
}
const subject = new star()
subject.add(new oberver('张三'))
const lisi = new oberver('李四')
subject.add(lisi)
subject.notify()
//移除观察者
subject.remove(lisi)
subject.notify()
# 发布订阅模式
- 发布订阅模式是一种消息通信模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
- 发布订阅模式可以理解为一个中介,它可以将客户端的请求转发给真正的目标对象,从而达到控制目标对象的目的
- 发布订阅模式可以将消息发送给多个观察者,而观察者模式可以将消息发送给单个观察者
- 发布订阅模式与观察者模式不同的是 观察者模式无法做到事件筛选
//发布订阅模式
//publisher 发布者
//subscriber 订阅者
const PubSub = {
list: {},
//发布通知函数
publish(type, data) {
if (!this.list[type]) throw new Error('没有订阅者')
this.list[type].forEach(item => item(data))
},
//订阅函数
subscribe(type, fn) {
(this.list[type] ||= []).push(fn)
},
//移除订阅函数
remove(type, fn) {
if (!this.list[type]) throw new Error('没有订阅者')
//不传fn将删除所有订阅者
if (!fn) return delete this.list[type]
this.list[type] = this.list[type].filter(item => item !== fn)
}
}
const text1 = (data) => {
console.log('1111', data)
}
const text2 = (data) => {
console.log('2222', data)
}
const text3 = (data) => {
console.log('3333', data)
}
//添加订阅者
PubSub.subscribe('aaa', text1)
PubSub.subscribe('aaa', text3)
PubSub.subscribe('bbb', text2)
//移除订阅者
PubSub.remove('aaa', text1)
//移除bbb的所有订阅者
PubSub.remove('bbb')
//发布通知
PubSub.publish('aaa', '我是aaa订阅者')
//1111 我是aaa订阅者
//3333 我是aaa订阅者
PubSub.publish('bbb', '我是bbb订阅者')//2222 我是bbb订阅者
# 模块模式
- 模块模式是一种将一组功能相对独立的类封装在一起的设计模式,可以将功能相对独立的类封装在一起,从而减少耦合
- 模块模式可以将功能相对独立的类封装在一起,从而减少耦合
- 模块模式可以将功能相对独立的类封装在一起,从而减少耦合
- 模块模式出现的本质是实现变量在模块中私有化,从而实现模块的功能相对独立
# es6之前通过闭包实现模块模式
const obj = (function () {
//私有变量
let _count = 0
return {
//公有方法
add() {
return ++_count
},
//公有方法
decr() {
return --_count
}
}
})()
console.log(obj.add())
console.log(obj.add())
注意
++_count 和 _count++ 区别
++_count 先自增,再返回自增后的值
_count++ 先返回值,再自增
# es6之后通过export 和import 实现模块模式
//模块js
let _count = 0
const add = () => {
return ++_count
}
const decr = () => {
return --_count
}
//导出模块成员
//未导出的成员无法被外界访问
export default {
add,
decr
}
# 桥接模式
- 桥接模式是将抽象部分与它的实现部分分离,使它们可以独立变化
- 使用场景:一个类存在两个或多个独立变化的维度 且这两个维度都需要拓展
//桥接模式
//封装动画类型
const AnimationType = {
bounce: {
show(ele) {
console.log(ele, 'bounce show')
},
hide(ele) {
console.log(ele, 'bounce hide')
}
},
slide: {
show(ele) {
console.log(ele, 'slide show')
},
hide(ele) {
console.log(ele, 'slide hide')
}
},
fade: {
show(ele) {
console.log(ele, 'fade show')
},
hide(ele) {
console.log(ele, 'fade hide')
}
}
}
//抽象实现方法
class Move {
constructor(type, ele) {
this.type = type
this.ele = ele
}
show() {
AnimationType[this.type].show(this.ele)
}
hide(ele) {
AnimationType[this.type].hide(this.ele)
}
}
//使用
const toast = new Move('slide', 'div')
toast.show()//div slide show
toast.hide()//div slide hide
const btn = new Move('fade', 'button')
btn.show()//button fade show
btn.hide()//button fade hide
# 组合模式
- 组合模式在对象间形成树型结构
//组合模式
//递归创建侧边栏
class SideBar {
constructor(root) {
this.file = root//保存当前根节点
this.children = []
}
addChild(child) {
this.children.push(child)
}
scan() {
console.log('当前创建', this.file)
this.children.forEach(item => {
// console.log(`当前创建${this.file}的子节点${item.file}`)
item.scan()
})
}
}
const root = new SideBar('root')//创建根节点
const home = new SideBar('首页')//创建一级节点
const change = new SideBar('编辑')//添加编辑一级节点
//添加一级节点
root.addChild(home)//添加一级节点
root.addChild(change)//添加一级节点
//添加二级节点
home.addChild(new SideBar('网站简介'))//添加首页二级节点
home.addChild(new SideBar('网站菜单'))//添加首页二级节点
change.addChild(new SideBar('修改'))//添加编辑二级节点
change.addChild(new SideBar('删除'))//添加编辑二级节点
root.scan()//扫描文件
# 命令模式
//命令模式
class Receiver {
//接受类
execute() {
console.log('接受者执行请求',)
}
}
class Command {
constructor(receiver) {
//命令类
this.receiver = receiver
}
execute() {
console.log('命令对象=》接受者如何处理')
this.receiver.execute()
}
}
class Invoker {
constructor(command) {
this.command = command
}
execude() {
console.log('发布请求')
this.command.execute()
}
}
const order = new Command(new Receiver())
const invoker = new Invoker(order)
invoker.execude()
# 模板方法模式
- 模板方法模式相当于ts中的抽象类
//模板方式模式
//动物类
class Animal {
constructor(name) {
this.name = name
}
say() {
throw new Error('抽象方法 必须被子类实现')
}
}
//狗类
class Dog extends Animal {
constructor(name, age) {
super(name);//super关键字表示父类的构造函数
this.age = age
}
say() {
console.log(this.name + '汪汪' + this.age)
}
}
const dog = new Dog('小黑', 18)
dog.say()
# 迭代器模式
- 迭代器模式是将一个可遍历的数据结构(例如集合或数组)转换为迭代器对象,迭代器对象可以使用next方法来获取数据
- 相当于循环
# 使用数组的方法循环对象
const peopsle = {
name: '小明',
age: 18,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.list.length) {
return {
done: false,
value: this.list[index++]
}
} else {
return {
done: true,
value: null
}
}
}
}
}
}
for (let item of peopsle) {
console.log(item)
}
# 职责链模式
- 链式调用
//职责链模式
//职责链验证
class Validator {
constructor(text) {
this.text = text
}
error(text) {
throw new Error(text)
}
//验证是否为空
isEmpty() {
if (this.text === '') {
return this.error('不允许为空')
}
return this
}
//验证长度
length() {
if (this.text.length < 5) {
return this.error('长度必须大于5')
}
return this
}
}
const res = new Validator('123456').length().isEmpty()
console.log(res)//{ text: '123456' }