js的第24天

设计模式


概述:

设计模式是一种固定的解决某个问题的一种方式,他是一个固定的模式(原理都是一样的),他不区分语言。常用的设计模式有23种,他分为三类(主要针对的是类和对象)。

设计模式分类

创建型(创建对象)单列模式,工厂模式

结构型(将多个小结构变成一个大结构)组合模式 代理模式 装饰器模式 适配器模式

行为型(对应类和对象的行为进行相关处理)观察者模式


所有模式

设计模式的原则

开闭原则

里式置换原则

单一原则

依赖倒置原则

接口隔离原则

迪米特原则

工厂模式讲解

根据对应的工厂方法生成对应的对象(生产相关对象)

function factory(name){

//手动构建对象

var obj = new Object()

//给对象进行属性赋值

obj.name = name

//手动返回对象

return obj

}

//调用工厂生产对象

let obj1 = factory('jack')

let obj2 = factory('tom')

单例模式

保证生成的对象实例只有一个

基础类

//基础类

class Person{

constructor(){

}

}

闭包实现

//闭包的单例模式

function closureSingleton(){

//接收返回对象的变量

var instance = null

return function(){

//如果当前的变量值为null

if(!instance){

//构建一个对象赋值给返回的变量

instance = new Person()

}

return instance

}

}

let single = closureSingleton()

let person1 = single()

let person2 = single()

console.log(person1==person2)

原型

//原型的单例

function prototypeSingleton(){

//判断原型上是否具备newPerson这个属性

if(!prototypeSingleton.prototype.instance){

prototypeSingleton.prototype.instance = new Person()

}

return prototypeSingleton.prototype.instance

}

let person3 = prototypeSingleton()

let person4 = prototypeSingleton()

console.log(person3 == person4)

静态属性

//静态属性单例

function staticSingleton(){

if(!staticSingleton.instance){

staticSingleton.instance = new Person()

}

return staticSingleton.instance

}

let person5 = staticSingleton()

let person6 = staticSingleton()

console.log(person5 == person6)

全局属性实现

window是浏览器global对象 但是其他的global对象不是window

//全局对象实现

function globalSingleton(){

if(!window.instance){

window.instance = new Person()

}

return window.instance

}

let person7 = globalSingleton()

let person8 = globalSingleton()

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)

}

//统一执行相关函数

execute(fnName,...arg){

//遍历list

this.list.forEach(item=>{

//执行对应的函数

item[fnName].call(this,...arg)

})

}

}

let combiner = new Combiner()

//将对象加入

combiner.push(new GoToHome())

combiner.push(new OpenComputer())

combiner.push(new PlayGame())

//执行action方法

combiner.execute('action')

组合模式在vue中的使用

Vue.use 方法(自动执行install)

install 方法

如果在use里面传入的是一个函数 那么他会把这个函数直接当成install 如果传的是对象 他会自动

去找里面的instal方法


模拟实现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 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)

fuction handler()_{

    console.log('点击了')

}

</script>

发布者 button

订阅者 javaScript引擎

处理 handler

分析这个事件的相关内容

发布者

订阅者

处理 

事件名和处理函数的关系

一对多一个事件可以有多个处理函数

相关关系

事件名和处理函数的关系 一对多 (一个事件可以有多个处理函数)click:[handler1,handler2]

发布者和事件名的关系

一对多 (一个发布者可以发布多个事件) {事件名:[处理函数]}

根据对应的事件监听机制来模拟观察者

事件监听的过程

事件发布(有对应的事件名)

事件执行(根据对应的事件名执行相关的处理函数)emit

事件取消(将对应的事件移除)off

代码构建

class obServer{

constructor(){

//{click:[handler1,handler2],mousemove:[handler1,handler2]}

this.obj = {}

}

//事件发布 事件名 处理函数

on(eventName,handler){

}

//事件执行 事件名 参数

emit(eventName,...arg){

}

//事件取消 事件名 处理函数

off(eventName,handler){

}

}

代码完善

on方法

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){

}

//事件取消 事件名 处理函数

off(eventName,handler){

}

}

emit方法

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){

}

}

off方法

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的类 这个类专门是用来做代理的 所以我们只需要掌握这个类的使用就

ok了

Proxy的使用

新建代理对象(通过proxy的构造)

let proxy = new Proxy(被代理对象,处理对象)

基础使用

//被代理对象

let obj = {

name:

'

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()

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容