设计模式
概述:
设计模式是一种固定解决某个问题的一种方式,他是同一个固定模式(原理都是一样的),他不区分语言.常用的设计模式有23钟,他分为三类(主要针对类和对象)
设计模式分类
- 创建型(创建对象) 单例模式 工厂模式
- 结构型(将多个小结构变成一个大结构)组合模式 代理模式 装饰器模式 适配器模式
- 行为型 (主要对应类和对象的行为进行相关处理) 观察者模式
- 开闭原则
- 里式置换原则
- 单一原则
- 依赖倒置原则
- 接口隔离原则
- 迪米特原则
工厂模式讲解
function factory(name){
//手动构建对象
var obj = new Object()
//给对象进行赋值
obj.name=name
手动返回对象
return obj
}
//调用工厂生产对象
let obj1 =factory('tom')
let obj2 =factory('哈哈')
单例模式
- 保证生成的对象实例只有一个
闭包实现
//基础类
class Person {
constructor(){
}
}
//闭包的单例模式
function closureSingleton(){
//接收返回对象的变量
var newPerson =null
return function(){
//如果当前的变量值为null
if(!newPerson){
//构建一个对象赋值给返回的变量
newPerson = new Person()
}
return newPerson
}
}
let single = closureSingleton()
let person1 = single ()
let person2 = single()
console.log(person1==person2)
静态属性
function staticSingleton(){
if(!staticSingleton.newPeson){
staticSingleton.newPeson = new Person()
}
return staticSingleton.newPeson
}
let person3 =staticSingleton()
let person4 = staticSingleton()
console.log (person3=person4)
原型的单例
function prototypeSingleton(){
if(!prototypeSingleton.prototype.instance){
prototypeSingleton.prototype.instance = new Person()
}
return prototypeSingleton.prototype.instance
}
let person5 = prototypeSingleton()
let person6 =prototypeSingleton()
console.log(person5==person6)
全局属性实现
window是浏览器global对象 但是其他的global对象不是window
function globaSingleton(){
if(!window.instance){
window.instance= new Person()
}
return window.instance
}
let person7 = globaSingleton()
let person8 = globaSingleton()
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
abc(obj){
this.list.push(obj)
}
//统一执行相关函数
execute (fn,...arg){
//遍历list
this.list.forEach((item)=>[
item[fn].call(this,...arg)
])
}
}
let combiner= new Combiner()
combiner.abc(new GoToHome())
combiner.abc(new OpenComputer())
combiner.abc(new PlayGame())
combiner.execute('action')
组合模式在Vue中的使用
- vue.use方法(自动执行install)
- install 方法
- 如果在use里面传入的是一个函数 那么他会直接把这个函数当成install 如果传的是对象 他会自动去找里面的install方法
模拟实现use和install
class Vue {
constructor() {
this.fnList = []
}
use(obj) {
//如果他是一个函数 那么这个函数直接当成install
if (typeof obj == 'function') {
this.fnList.push(obj)
}
//如果他是对象就找里面的install
if (typeof obj == 'object') {
this.fnList.push(obj.install)
}
//调用执行
this.execute()
}
execute() {
//遍历对应的函数列表
this.fnList.forEach((fn) => {
fn()
})
}
}
var vue = new Vue()
vue.use({
install() {
console.log('你好')
}
})
vue.use({
install() {
console.log('世界')
}
})
vue.use(() => {
console.log('哈哈哈哈')
})
装饰器模式
场景
我现在有三个车的类,这个三个车他都有对应的跑的方法,现在我需要给这个三个车加一个飞的方法(设计模式开闭原则,一个类一旦写完了不允许内部再进行修改),这个时候就可以使用对应的装饰器模式。
概述
装饰器模式是在不影响原本类的基础上,进行功能扩展.他又叫做包装模式.
实现
基础类
class Car{
constructor(){
}
run(){
console.log('跑');
}
}
包装类
class Decoator{
constructor(aa){
this.car=aa
}
fly(){
console.log('飞');
this.car.run()
}
}
var decoator = new Decoator(new Car())
decoator.fly()
console.log(decoator)
// new Decoator(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],mousemove:[handler1,handler2]}
this.obj = {}
}
//事件发布 事件名 处理函数
on(eventName,handler){
}
//事件执行 事件名 参数
emit(eventName,...arg){
}
//事件取消 事件名 处理函数
off(eventName,handler){
}
}
代码完善
class ObServer{
constructor(){
//{click:[handler1,handler2],mousemove:[handler1,handler2]}
this.obj = {}
}
//事件发布 事件名 处理函数
on(eventName,handler){
//判断这个eventName是否存在
//如果当前的事件名不存在 给他赋值一个空数组
if(!this.obj[eventName]){
this.obj[eventName] = []
}
//将事件添加进去
this.obj[eventName].push(handler)
}
//事件执行 事件名 参数
emit(eventName,...arg){
//判断这个eventName是否存在
if(!this.obj[eventName]) return
//获取这个事件里面所有的处理函数 执行这些处理函数
//遍历对应的处理函数数组
this.obj[eventName].forEach(handler=>{
//执行对应的处理函数 传入参数
handler.call(this,...arg)
})
}
//事件取消 事件名 处理函数
off(eventName,handler){
//判断这个eventName是否存在
if(!this.obj[eventName]) return
//遍历对应的eventName里面处理函数数组 找到匹配的handler将他删除
this.obj[eventName].forEach((v,i)=>{
if(Object.is(v,handler)){
this.obj[eventName].splice(i,1)
}
})
}
}
注意事项
- emit执行他可以传参传给对应的on方法里面处理函数(vue中父传子的实现及bus传值的实现)
- off调用一定要emit之前
- 观察者模式是vue2底层实现
代理模式(☆)
概述:
在不改变原本的类的基础上,对于对象功能加强,代理模式代理出来的是对象.(代理模式低耦合)
示例
- 你家里需要装修,但你不想动手 这个时候你就会找一个装修队(代替你装修)
- 代理对象和被代理对象是两个不同的对象,但是实际操作的内容是一个
核心关键词
- 对代理对象 你
- 代理对象 装修对象
- 操作内容 你的房子
代理的实现
在js中es7新增了一个Proxy的类 ,这个类专门用来做代理.
Proxy的使用
新建的代理对象(通过proxy的构造)
let proxy = new Proxy(被代理对象,处理对象)
基础使用
//被代理对象
let obj = {
name: 'jack',
age: 18
}
//通过proxy来新建代理对象
//Proxy的构造函数需要传入被代理对象 以及相关的处理对象
//对于handler他是对于所有的属性进行操作
//get在获取属性的时候调用 他返回的结果就是你接收的结果
//set对于设置属性的时候调用
let proxy = new Proxy(obj,{
//目标对象(被代理对象) 属性(实际访问的属性) 代理对象(当前proxy)
get(target,attribute,proxyObj){ //这个里面返回的值就是当前的获取值
console.log('调用了get');
if(attribute == 'name'){
return '姓名为'+target[attribute]
}else if(attribute == 'age'){
return target[attribute]+'岁'
}
return target[attribute]
},
//目标对象(被代理对象) 属性(实际访问的属性)值 代理对象(当前proxy)
set(target,attribute,value,proxyObj){
console.log('调用了set');
target[attribute] = value
},
//定义属性 赋值新的属性 目标对象 属性名 属性的详情信息
defineProperty(target,attribute,descriptor){
console.log('新的属性定义');
},
//删除属性 delete的时候
deleteProperty(target,attribute,proxyObj){
console.log('删除属性');
delete target[attribute]
}
})
proxy的处理对象的四大属性
- get 访问属性的时候调用
console.log( proxy.name); //调用get
console.log( proxy.age); //调用get
- set 设置属性的时候调用
proxy.name = '你好' //调用set
- defineProperty 定义属性的时候调用
Object.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()
}
}
//实际使用的是对于的适配的内容
new Adaptive().fn()