day24 设计模式
概述 :
设计模式是一种解决某个问题的固定模式 ( 原理都是一样的 ) , 它不区分语言 . 常用的设计模式有23种 , 他分为三类 ( 主要针对的是类和对象 )
设计模式的分类
-
创建型 ( 创建类和对象 , 主要是创建对象 ) 如单例模式 、工厂模式 =>厂家
-
行为型 ( 主要是对类和对象的方法进行操作 ) 如 观察者模式 =>工人
-
结构型 ( 将多个小结构变成一个大结构 ) 如组合模式 、代理模式 、 装饰器模式 、 适配器模式 =>搭结构的
设计模式的原则
-
开闭原则
-
里氏置换原则
-
单一原则
-
依赖倒置原则
-
接口隔离原则
-
迪米特原则
工厂模式讲解
-
根据工厂方法生成对象 ( 生成相关对象 )
function factory(name){ //手动构建对象 var obj = new Objcet() //给对象进行赋值属性 obj.name = name //手动返回对象 return obj } //调用工厂生产对象 let obj1 = factory('jack') let obj2 = factory('tom')
单例模式
-
保证生成的对象的实例 只有一个
闭包实现
//基础类 class Person { constructor(){ } }
原型实现
//基础类 class Person { constructor(){ } } //原型的单例 function prototypeSingleton(){ //判断原型上是否具备newPerson这个属性,没有就赋值,有就直接返回 if(!prototypeSingleton.prototype.newPerson){ prototypeSingleton.prototype.newPerson = new Person() } return prototypeSingleton.prototype.newPerson } let person3 = prototypeSingleton() let person4 = prototypeSingleton() console.log(person3 == person4)
静态属性实现
//基础类 class Person { constructor(){ } } //静态属性的单例 function staticSingleton(){ //判断静态属性上是否具备newPerson这个属性,没有就赋值,有就直接返回 if(!staticSingleton.newPerson){ staticSingleton.newPerson = new Person() } return staticSingleton.newPerson } let person5 = staticSingleton() let person6 = staticSingleton() console.log(person5 == person6)
全局属性实现
-
window是浏览器的global对象 , 但是其他的global对象 有可能不是window
//基础类 class Person { constructor(){ } } //全局对象实现 function globalSingleton(){ if(!window.newPerson){ window.newPerson = new Person() } return window.newPerson } let person7 = staticSingleton() let person8 = staticSingleton() console.log(person7 == person8)
组合模式 (*)
-
将多个对象的相同方法和属性组合到一块 ( 将不同的方法组合在一块执行 )
基础内容
class GoToHome{ constructor(){ this.action=()=>{ console.log('回家') } } } class OpenComputer{ constructor(){ this.action=()=>{ console.log('打开电脑') } } } class PlayGame{ constructor(){ this.action=()=>{ console.log('玩游戏') } } } new GoToHome().action() new OpenComputer().action() new PlayGame().action()
将多个方法组合在一块 , 统一调用
class Combiner{ constructor(){ this.list = [] } //加入,把对应的对象添加到list中 push(obj){ this.list.push(obj) } //执行 ,执行函数,可传多个参数 excute(fnName,...arg){ //遍历数组 this.list.forEach((item)=>{ //执行对应的函数并传参 item[fnName]() // item[fnName].call(this,..arg) }) } } let combiner = new Combiner() //将对象加入 combiner.push(new GoToHome()) combiner.push(new OpenComputer()) combiner.push(new PlayGame()) //执行action方法 combiner.excute('action')
组合模式在vue中的使用
-
Vue.use (自动执行install)
-
install 方法
-
如果在use里面传入的是一个函数,那么它会把这个函数直接当成install,如果传的是对象,它会自动去找里面的install方法
模拟实现use和install
class Vue{ constructor(){ this.fnList=[] } use(obj){ //如果obj是一个函数,那么就把这个函数直接当成install执行 if(typeof obj =='function'){ this.fnList.push(obj) } //如果obj是一个对象,那就去对象内找install方法 if(typeof obj=='object'){ this.fnList.push(obj.install) } //调用执行方法 this.excute() } excute(){ //遍历对应的函数列表并执行 this.fnList.forEach((fn)=>{ fn() }) } } let vue = new Vue() //调用use方法 vue.use(()=>{ console.log('hello') }) vue.use({ install(){ console.log('world') } })
装饰器模式 (又叫做包装模式)
场景:
我现在有三个车的类 , 这三个车都有对应的跑的方法 , 现在我先给这三个车添加一个飞的方法 ( 设计模式开闭原则 , 一个类一旦写完了不允许内部再进行修改 ),这个时候就可以使用装饰器模式
概述 :
装饰器模式是在不影响原本类的基础上,进行功能扩展,它又叫做包装模式
实现
基础类
class Car{ constructor(){ run(){ console.log('跑') } } }
包装类
class Decorator{ constructor(car){ //将基础类传入包装类 this.car=car } fly(){ console.log('飞') this.car.run() } } new Decorator(new Car()).fly()
在typeScript中有个对应的装饰器修饰 @Decorator
观察者模式 (*)
概述 :
观察者模式是前端最常用的模式,它又被称为发布者-订阅者模式。它的核心是一个发布者,它有自己的订阅者,当发布者发布信息时,订阅者会收到信息然后可以进行处理。
观察者模式的核心内容
-
发布者
-
订阅者
-
相关处理
其实我们的事件就是一个观察者模式(事件发布者,事件监听者,事件处理)
element.addEventListener('事件名',处理函数) element.removeEventListener('事件名',处理函数)
简单的事件示例
<button>点我</button> <script> let btn = document.querySelector('button') btn.addEventListener('click',handler) function handler(){ console.log('点击了') } </script>
-
发布者 button
-
订阅者 javaScript引擎
-
处理 handler
分析事件的相关内容
-
元素 ( 发布者 )
-
事件名
-
处理函数
相关关系
-
事件名和处理函数是一对多的关系,就是一个事件可以有多个处理函数 click : [handler1,handler2]
-
发布者和事件名的关系也是一对多,就是一个发布者可以发布多个事件
根据事件监听机制来模拟观察者
事件监听的过程
-
事件发布 (有对应的事件名)on
-
事件执行 (根据对应的事件名执行相关的处理函数)emit
-
事件取消 (将对应的事件移除)off
代码构建
class ObServer{ constructor(){ //{click:[handler1,handler2],mouseenter:[handler1,handler2]} this.obj = {} } //事件发布 事件名,处理函数 on(eventName,handler){ } //事件执行 事件名,参数 emit(eventName,...arg){ } //事件取消 事件名,处理函数 off(eventName,handler){ } }
代码完善
on方法
class ObServer{ constructor(){ this.obj = {} } //事件发布 事件名,处理函数 on(eventName,handler){ //判断是否有事件,没有就先赋值一个空数值 if(!this.obj[eventName]){ this.obj[eventName]=[] } //再将事件添加进去 this.obj[eventName].push(handler) } //事件执行 事件名,参数 emit(eventName,...arg){ } //事件取消 事件名,处理函数 off(eventName,handler){ } }
emit方法
class ObServer{ constructor(){ this.obj = {} } //事件发布 事件名,处理函数 on(eventName,handler){ //判断是否有事件,没有就先赋值一个空数值 if(!this.obj[eventName]){ this.obj[eventName]=[] } this.obj[eventName].push(handler) } //事件执行 事件名,参数 emit(eventName,...arg){ //判断是否有事件,没有就直接返回 if(!this.obj[eventName]) return //有就遍历它的处理函数,再执行并传参 this.obj[eventName].forEach((item)=>{ item.call(this,...arg) //item() }) } //事件取消 事件名,处理函数 off(eventName,handler){ } } let obServer = new ObServer() obServer.on('click',(params1,params2)=>{ console.log(111+params1+params2) }) obServer.emit('click','10','hi')//121hi
off方法
class ObServer{ constructor(){ this.obj = {} } //事件发布 事件名,处理函数 on(eventName,handler){ //判断是否有事件,没有就先赋值一个空数值 if(!this.obj[eventName]){ this.obj[eventName]=[] } this.obj[eventName].push(handler) } //事件执行 事件名,参数 emit(eventName,...arg){ //判断是否有事件,没有就直接返回 if(!this.obj[eventName]) return //有就遍历它的处理函数,再执行并传参 this.obj[eventName].forEach((item)=>{ item.call(this,...arg) }) } //事件取消 事件名,处理函数 off(eventName,handler){ //判断是否有事件,没有就直接返回 if(!this.obj[event]) return //遍历它的处理函数 this.obj[eventName].forEach((item,index)=>{ //如果函数一致就根据下标删除事件 if(Object.is(item,handler)){ this.obj[eventName].splice(index,1) } }) } } let obServer = new ObServer() obServer.on('click',(params1,params2)=>{ console.log(111+params1+params2) }) function fn(){ console.log('aloha') } obServer.on('dbclick',fn) obServer.off('dbclick',fn)//删除 obServer.emit('dbclick')//undefined obServer.emit('click','10','hi')//121hi
注意事项
-
emit执行 , 他可以传参传给对应的on方法里面的处理函数 ( vue中父传子的实现 及bus传值的实现 )
-
off调用一定要在emit执行之前
-
观察者模式是vue2底层实现
代理模式(*)
概述 :
在不改变原本的类的基础上 , 对于对象进行功能加强 , 代理模式代理出来的是对象 , 代理模式低耦合
示例 :
你家里需要装修 , 但是你不想动手 , 这个时候你就会找一个装修队代替你装修
代理对象和本身被代理对象是两个不同的对象 , 但是实际操作的内容是同一个 ,
核心关键词
-
代理对象 --装修队
-
被代理对象 --我
-
操作内容 --房子
代理的实现
在js中 , es7新增了一个Proxy的类 , 这个类是专门来做代理的 , 所以我们只需要掌握这个类的使用就可以了~
Proxy的使用
新建代理对象 (通过proxy的构造)
let proxy = new Proxy(被处理对象,相关的处理对象)
基础使用
//被代理对象 let obj = { name:'jack', age:18 } //通过Proxy来新建代理对象 //Proxy的构造函数需要传入被代理对象 以及相关的处理对象 let proxy = new Proxy(obj,{ //get是在获取的时候调用,它返回的结果就是你接收的结果 //目标对象(被代理对象),属性(实际访问的属性),代理对象(当前proxy) get(target,attribute,proxyObj){ console.log('调用了get') if(attribute == 'name'){ return '姓名为'+target[attribute] }else if(attribute == 'age'){ return target[attribute]+'岁' } return target[attribute] }, //set是设置的时候调用 //目标对象(被代理对象),属性(实际访问的属性),属性值,代理对象(当前proxy) set(target,attribute,value,proxyObj){ console.log('调用了set') target[attribute] = value }, //definedProperty是定义属性,也就是赋值新的属性 //目标对象(被代理对象),属性(实际访问的属性),属性的详情信息 defineProperty(target,attribute,descriptor){ console.log('定义了新的属性') console.log(descriptor) }, //deleteProperty是删除属性的时候调用 //目标对象(被代理对象),属性(实际访问的属性),代理对象(当前proxy) deleteProperty(target,attribute,proxyObj){ console.log('删除属性') delete target[attribute] } }) console.log(proxy.name)//调用了get,姓名为jack console.log(proxy.age)//调用了get,18岁 proxy.name = 'rose' //调用了set Object.defineProperty(proxy,'sex',{ configurable:true,//是否可以删除 encmerable:true,//是否可以遍历 value:'男', writable:true//是否可以修改 }) delete proxy.name//删除属性
proxy的处理对象的四大属性
-
get 访问属性的时候调用
console.log(proxy.name) //调用了get console.log(proxy.age) //调用了get
-
set 设置属性的时候调用
proxy.name = '你好' //调用set
-
defineProperty 定义属性的时候调用
Objtect.defineProperty(proxy,'sex',{ configurable:true, //是否可以删除 enumerable:true, //是否可以遍历 value:'男', //属性值 writable:true //是否可以改变 }) deleteProperty 删除属性的时候调用 delete proxy.name //调用deleteProperty
总结
-
Proxy 是ES7新增的一个类 , 它返回的是一个对象
-
Proxy里面传入被处理对象和对应的处理对象
-
处理对象包含4个方法 ( get , set ,defineProperty , deleteProperty )
-
Proxy里面实际操作是被代理对象 ( 如果在里面操作代理对象会造成栈溢出 )
-
代理对象和被代理对象不是一个对象 , 但是操作的内容是同一个
-
Proxy是vue3的底层实现之一
适配器模式
概述 :
将旧的内容进行新的适配 , 在原本的基础上做兼容处理
常用的业务场景 :
-
旧的接口替换成新的接口 ( 在不改变原本内容的情况下完成替换 )
-
表单验证 ( 根据场景来切换相关的验证 )
示例:
-
家庭用电最大功率为22v
-
接入的电压220v
-
中间就需要继电器 ( 适配口 )
代码实现
class Phone{ constructor(){ } fn(){ return 22 } } class Adaptive{ constructor(){ } fn(){ return '220转为'+new Phone().fn() } } //实际使用的是对应的适配的内容 console.log(new Adaptive().fn())标签:obj,log,对象,day24,eventName,事件,console,设计模式 From: https://www.cnblogs.com/itlulu/p/16867662.html