概述:
设计模式是一种固定的解决某个问题的一种方式,他是一个固定的模式(原理都是一样的),他不区分语言。常用的设计模式有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)
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){
}
}
代码完善
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: '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的底层实现之一
适配器模式
概述
将旧的的内容进行新的适配,在原本的基础上做兼容处理。
常用的业务场景
-
旧的接口替换新的接口(在不改变原本内容的情况下完成替换)
-
表单验证 (根据场景切换相关的验证)
代码实现
class phone{
constructor(){
}
fn(){
return 22
}
}
class Adaptive{
constructor(){
}
fn(){
return '220转为'+new Phone().fn()
}
}
//实际使用的是对于的适配的内容
new Adaptive().fn()