美文网首页js css html
JavaScript--设计模式

JavaScript--设计模式

作者: 蒜泥捣莓 | 来源:发表于2022-08-24 19:33 被阅读0次

    一、概述

    设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。

    二、原则

    S – Single Responsibility Principle 单一职责原则

    • 一个程序只做好一件事
    • 如果功能过于复杂就拆分开,每个部分保持独立

    O – OpenClosed Principle 开放/封闭原则

    • 对扩展开放,对修改封闭
    • 增加需求时,扩展新代码,而非修改已有代码

    L – Liskov Substitution Principle 里氏替换原则

    • 子类能覆盖父类
    • 父类能出现的地方子类就能出现

    I – Interface Segregation Principle 接口隔离原则

    • 保持接口的单一独立
    • 类似单一职责原则,这里更关注接口

    D – Dependency Inversion Principle 依赖倒转原则

    • 面向接口编程,依赖于抽象而不依赖于具体
    • 使用方只关注接口而不关注具体类的实现

    三、分类

    • 创建型
      1. 单例模式
      2. 原型模式
      3. 工厂模式
      4. 抽象工厂模式
      5. 建造者模式
    • 结构型
      1. 适配器模式
      2. 装饰器模式
      3. 代理模式
      4. 外观模式
      5. 桥接模式
      6. 组合模式
      7. 享元模式
    • 行为型
      1. 观察者模式
      2. 迭代器模式
      3. 策略模式
      4. 模板方法模式
      5. 职责链模式
      6. 命令模式
      7. 备忘录模式
      8. 状态模式
      9. 访问者模式
      10. 中介模式
      11. 解释器模式

    四、示例

    4.1单例模式

    概述:

    一个类只有一个实例(生成出来对象永远只有一个实例),并提供一个访问它的全局访问点。

    实现过程:

    • 构建一个函数返回一个对象实例
    • 用一个声明一次变量来控制这个对象实例的生成。
    • 如果该变量里已有一个对象,则直接返回,如果没有,则生成,生成给变量存起来。

    应用:登录框

    闭包实现

    function single(){
        let obj //标识
        return function(){
            if(!obj){ //判断是否为undefined
                obj = new Object()
            }
            return obj
        }
    }
    let singleObj = single()
    let obj1 = singleObj()
    let obj2 = singleObj()
    console.log(obj1===obj2)//true
    

    原型实现

    function singlePrototype(){
        if(!Object.prototype.instance){
            Object.prototype.instance = new Object()
        }
        return Object.prototype.instance
    }
    let obj1 = singlePrototype()
    let obj2 = singlePrototype()
    console.log(obj1===obj2) //true
    

    static实现

    function singleStatic(){
        if(!Object.instance){
            Object.instance = new Object()
        }
        return Object.instance
    }
    let obj1 = singleStatic()
    let obj2 = singleStatic()
    console.log(obj1===obj2)
    

    全局变量实现

    function singleWindow(){
        if(!window.instance){
            window.instance = new Object()
        }
        return window.instance
    }
    let obj1 = singleWindow()
    let obj2 = singleWindow()
    console.log(obj1===obj2)
    
    4.2工厂模式

    概述:

    工厂模式生产对象的,以一个工厂方法来生产对应的对象。

    实现过程:

    • 手动构建对象
    • 手动给对象设置属性
    • 手动返回对象
    function factory(){
        let obj = new Object()
        obj.name = 'jack'
        return obj
    }
    
    4.3组合模式

    概述:

    将对应多个相同名字方法 放在一个地方统一调用。

    实现:

    class SayHello{
        constructor(){
            
        }
        say(){
            console.log('hello')
        }
    }
    class SayHi{
        constructor(){
            
        }
        say(){
            console.log('hi')
        }
    }
    class SayBay{
        constructor(){
            
        }
        say(){
            console.log('baybay')
        }
    }
    

    以上的三个类 分别都具备一个名为say的方法 如果需要调用的话 那么是一个个的对象进行调用而不能统一调用,如果我需要他统一调用,这个时候我们就可以使用组合模式。

    class Combiner{
        constructor(){
            //容器来保存对应的对象
            this.objs = []
        }
        push(obj){
            //添加对象
            this.objs.push(obj)
        }
        excute(fnName){
            //执行对应的方法
            this.objs.forEach(item=>{
                item[fnName]()
            })
        }
    }
    //新建组合模式对象
    let combiner = new Combiner()
    //传入对应统一调用的对象
    combiner.push(new SayHello())
    combiner.push(new SayHi())
    combiner.push(new SayBay())
    //执行对应的方法
    combiner.excute('say')
    

    组合模式在vue中使用

    use和install

    vue.use()为注册全局插件所用,接收函数或者一个包含install属性的对象为参数,如果参数带有install就执行install, 如果没有就直接将参数当install执行, 第一个参数始终为vue对象, 注册过的插件不会重新注册

    4.4观察者模式

    概述

    • 观察者模式(obServer)他又被称为发布-订阅者模式,消息模式等。
    • 定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使它们能够自动更新自己,当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

    场景:

    • DOM事件
    document.body.addEventListener('click', function() {
        console.log('hello world!');
    });
    document.body.click()
    
    • vue响应式

    实现:

    class ObServer{
        constructor(){
            //事件和对应的处理函数存储的容器
            this.arg = {} //click:[fn,fn1]
        }
        on(){//发布事件
        
        }
        emit(){//执行处理函数
            
        }
        off(){//取消事件
        
        }
    }
    

    on方法实现

    class ObServer{
        constructor(){
            this.arg = {} //{click:[fn,fn1]}
        }
        on(eventName, handler) { //发布事件 事件名  处理函数
            if (!this.arg[eventName]) { //没有这个事件
                this.arg[eventName] = [] //初始化里面为空数组
            }
            this.arg[eventName].push(handler) //将对应的函数追加
        }
        emit(){//执行处理函数
    
        }
        off(){//取消事件
    
        }
    }
    

    emit方法实现

    class ObServer{
        constructor(){
            this.arg = {} //{click:[fn,fn1]}
        }
        on(eventName, handler) { //发布事件 事件名  处理函数
            if (!this.arg[eventName]) { //没有这个事件
                this.arg[eventName] = [] //初始化里面为空数组
            }
            this.arg[eventName].push(handler) //将对应的函数追加
        }
        emit(eventName, params) { //执行处理函数
            if (!this.arg[eventName]){
                return
            }
            //会将里面的处理函数都执行
            //遍历对应的处理函数数组
            this.arg[eventName].forEach(fn => {
                //将参数传入执行
                fn.call(this, params)
            })
        }
        off(){//取消事件
    
        }
    }
    

    off方法实现

    class ObServer {
        constructor() {
            this.arg = {} //{click:[fn,fn1]}
        }
        on(eventName, handler) { //发布事件 事件名  处理函数
            if (!this.arg[eventName]) { //没有这个事件
                this.arg[eventName] = [] //初始化里面为空数组
            }
            this.arg[eventName].push(handler) //将对应的函数追加
        }
        emit(eventName, params) { //执行处理函数
            if (!this.arg[eventName]){
                return
            }
            //会将里面的处理函数都执行
            //遍历对应的处理函数数组
            this.arg[eventName].forEach(fn => {
                //将参数传入执行
                fn.call(this, params)
            })
        }
        off(eventName, handler) { //取消事件
            if (!this.arg[eventName]) {
                return
            }
            //将这个对应的fn删除
            if (this.arg[eventName].length == 1) {
                delete this.arg[eventName]
            } else {
                let i
                this.arg[eventName].forEach((item, index) => {
                    if (Object.is(item, handler)) {
                        i = index
                    }
                })
                this.arg[eventName].splice(i, 1)
            }
        }
    }
    

    扩展在观察者emit方法传入参数 传到对应的on里面的处理函数 vue里面子传父的实现

    4.5代理模式

    概述:

    代理模式利用一个代理对象来处理当前对象事情

    假设当A 在心情好的时候收到花,小明表白成功的几率有60%,而当A 在心情差的时候收到花,小明表白的成功率无限趋近于0。小明跟A 刚刚认识两天,还无法辨别A 什么时候心情好。如果不合时宜地把花送给A,花被直接扔掉的可能性很大,这束花可是小明吃了7 天泡面换来的。但是A 的朋友B 却很了解A,所以小明只管把花交给B,B 会监听A 的心情变化,然后选择A 心情好的时候把花转交给A,
    

    es7新增一个类 Proxy 他就是用于代理的,他是vue3的底层实现

    Proxy构造函数

    new Proxy(目标对象,handler处理对象)
    

    对应的处理对象有4大方法

    • get属性 获取对应的代理对象的值调用
    • set属性 设置代理对象的值调用
    • deleteProperty 删除代理对象的属性调用
    • has属性 在遍历的时候调用
    //目标对象
    let target = {name:'张三',age:18,say(){
        console.log('hello');
    }}
    //利用proxy产生代理对象
    let proxy = new Proxy(target,{
        get(target,property,proxy){ //表示目标对象  表示属性名 表示代理对象
            console.log('get调用了');
            //访问值的时候
            if(property =='name'){
                return '我的名字是'+target[property]
            }
            if(property =='age'){
                return '我的年纪是'+target[property]+'岁'
            }
        },
        set(target,property,value){
            //设置值的时候 进行相关操作
            console.log(property);
            console.log(value);
            target[property] = value
        },
        deleteProperty(target,property,proxy){
            //删除属性的时候
            console.log('delete调用了');
            delete target[property]
        },
        has(target,property){
            //in的时候调用 必须返回boolean 强制转换为boolean类型
            console.log('has调用了');
            console.log(property);
            return property in target
        },
        apply(target,property){ //函数调用触发
            console.log('apply调用了');
        }
    })
    //读取代理对象的属性的时候 会自动调用get方法 他的值是get方法返回的值
    console.log(proxy.age); //调用get
    proxy.name = 'jack' //调用set
    console.log(proxy); 
    delete proxy.name //调用deleteProperty
    console.log('name' in proxy); //某个东西是否在某个东西里面返回boolean
    console.log(proxy.say); //代理只第一层
    

    apply对应的方法

    function sum(a, b) {
        return a + b;
    }
    
    const handler = {
        apply: function (target, thisArg, argumentsList) { //目标对象 当前this 参数数组
            console.log('apply调用了');
            // expected output: "Calculate sum: 1,2"
    
            return target(argumentsList[0], argumentsList[1]) * 10;
        }
    };
    
    const proxy1 = new Proxy(sum, handler);
    
    console.log(sum(1, 2));
    // expected output: 3
    console.log(proxy1(1, 2));
    // expected output: 30
    
    4.6装饰者模式

    概述:

    • 动态地给某个对象添加一些额外的职责,,是一种实现继承的替代方案
    • 在不改变原对象的基础上,通过对其进行包装扩展,使原有对象可以满足用户的更复杂需求,而不会影响从这个类中派生的其他对象

    实现:

    //原本类
    class Car{
        constructor(){
            
        }
        run(){
            console.log('车在跑')
        }
    }
    //增强的类
    class Decorater{
        constructor(car){
            this.car = car
        }
        run(){
            console.log('我边吃饭边开车')
            this.car.run()
        }
    }
    new Decorater(new Car()).run()
    

    扩展:es7新增一个装饰器 其实就是装饰器模式的封装

    4.7适配器模式

    概述

    将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。

    实现:

    let phone = {
        fn(){
            retrun '5v'
        }
    }
    class Target{
        constructor(){
        }
        fn(){
            let v = phone.fn()
            return '220转换为'+v
        }
    }
    new Target().fn()
    

    应用:

    • 整合第三方SDK
    • 封装旧接口

    相关文章

      网友评论

        本文标题:JavaScript--设计模式

        本文链接:https://www.haomeiwen.com/subject/ydpogrtx.html