前端常用设计模式(前端面试设计模式)-大白话讲解🎉🎉🎉

4,306 阅读11分钟

(前端常用设计模式)。我们在面试的时候很多情况都需要手写代码,所以我们写都时候尽量用es6,且里面要用设计模式。这样评分可以达到优秀,如果只是手写实现出功能那么我们只能是及格(主要面试和别人竞争)

游泳健身了解一下:github

尽量用大白话讲一下设计模式,设计模式不是看一次就够的,也不是这次记住就够的。将设计模式融入到我们的各个代码里,这个才是我们的目标,下面我会说一下我们常用的一些,但是我们却不知道这些也是添加了这些设计模式。

    面向对象
    设计原则
    工厂模式,抽象工厂模式
    状态模式
    代理模式
    单例模式
    观察者模式(和订阅发布)
    适配器模式
    外观模式

面向对象

什么是面对对象编程(千万不要回答万物都是对象然后就没下句了),面向对象就是你不需要关注使用过程,知道去使用就可以。(希望你也能用你的方式讲出来)

大白话:小红住在1班,一班是一个整体,一班里面有小希,小溪,小西,学校分配任务给1班叫一班去操场大扫除,班级有大扫除的方法。我们先构建一班这个类 class 不需要去关注1班里面的小红,小希,小溪,小西,直接new 1班() 然后使用当前的大扫除方法。

// 自己实现请用英文 请养成良好的编程习惯。(=。=)
class 1班class{
    constructor(){
        this.小红 = new 小红()
        this.小希 = new 小希()
        this.小溪 = new 小溪()
        this.小西 = new 小西()
    }
    一起去大扫除(){
        ...
    }
}
let 1班 = new 1班class()
1班.一起去大扫除()

还是不懂的话就看下面的

你想吃蛋炒饭

    你拿出鸡蛋,米饭,锅油然后炒了炒最后出现了蛋炒饭。(不是面向对象,这个是面向过程)
    你去饭店然后点了一份蛋炒额(真香!还是饭店里面大厨做的好吃😋)(这个是面向对象,你并不需要关注这个蛋炒饭是怎么做的)
    
    (哈哈哈哈哈😄)
    你走到一栋大楼面前(哇!钢筋,水泥,木板。。。)(面向过程)
    你走到一栋大楼面前(哇!大楼)(面向对象)

这个时候你应该知道啥是面对对象了,下面对比一下面向对象和面向过程

    1.面向过程
        其实是面向对象的底层,只是我们把这个过程写在了对象的类上了,面向过程这样的使用方式为啥不好,
        (优势也是有的性能好,不用去new 一个实例,减少性能开销)
        1.不容易维护(好几千行的代码都搞一起了,写过的都知道)
        2.不容易扩展(好几千行的代码都搞一起了,写过的都知道)
        3.不容易复用(好几千行的代码都搞一起了,写过的都知道)
    
    2.面向对象
        面向对象有继承,封装,多态的特性,面向对象优势很容易看出,
        1.容易维护
        2.容易扩展
        3.容易复用

面向对象的继承:

es6继承

    class A{
        Afun(){
    
        }
    }
    class B extends A{
    
    }
    let b = new B()
    b.Afun() // b 可以继承A的方法

普通常用的继承

    原型链继承
    function A(){}
    A.prototype.Afun = function() {}
    function B(){}
    B.prototype = new A()
    let b = new B()
    b.Afun() // b通过原型链 prototype 可以调用到A的方法
    // 问题,当前并不能执行A的方法只是继承到来A到方法(而且A方法居然在之前就使用过一次)
    
    构造函数
    function A(a){this.a = a}
    A.prototype.Afun = function() {console.log(123)}
    function B(){A.call(this,1)}
    let b = new B()
    b.a // 可以获取到当前
    // 问题,当前并不能继承A的方法,只能使用A方法
    
    混合使用
    function A(a){
        this.a = a
    }
    A.prototype.Afun = function() {
        console.log('Afun')
    }
    function B(){
        A.call(this,1)
    }
    B.prototype             = A.prototype
    B.prototype.constructor = A
    let b = new B()
    console.log(b.a)
    b.Afun()
    // 这样就可以使用当前A的方法,不懂的小伙伴可以自己码一下试试

面向对象对封装(隐藏对象对属性和方法,对外部提供属性和使用方法),提高复用性和安全性。

面向对象对多态(就是多种状态)(同一种行为有不同表现的能力)js 并没有提供什么模版啥的,我们可以看下下面的

class A{
      toString(){
          console.log('A')
      }
  }
  class B extends A{
      toString(){
          console.log('B')
          super.toString();
      }
  }
  class C extends A{
      toString(){
          console.log('C')
          super.toString();
      }
  }
  
  let b = new B()
  b.toString()
  let c = new C()
  c.toString()
// B和C都继承A,都有toString方法(同种行为),但是他们的能力(状态)是不同的(可以有不同的方式)

设计原则

    1.单一职责原则(类功能要单一,不能什么功能都往类里面写)
    2.开放封闭原则(对扩展开放,对修改封闭,可以进行对功能对扩展,但是减少对功能对修改)
    3.里式替换原则(前端不咋用)
    4.依赖倒置原则(前端不咋用)
    5.接口分离原则(前端不咋用,都没有接口)
    需要了解后面的设计原则的可以自行百度

工厂模式,抽象工厂模式

工厂模式,通过传入不同短参数达到实例化不同的对象这一目的。比如: 有一天你去餐厅想吃宫保鸡丁,然后餐厅给你实例化了宫保鸡丁。

// 饭店的class 自己实现请用英文 请养成良好的编程习惯。(=。=)
class Restaurant{
    constructor(type){
        switch (type) {
            case '宫保鸡丁':
                return new 宫保鸡丁()
                break;
            case '西红柿炒鸡蛋':
                return new 西红柿炒鸡蛋()
                break;
            default:
                throw "我们餐厅没有你要的菜不好意思,请出门左拐!!!" 
                break;
        }
    }
}
let mincedChicken= new Restaurant('宫保鸡丁')
let scrambledTomato = new Restaurant('西红柿炒鸡蛋')

抽象工厂模式,就是定制了实例的结构,比如宫保鸡丁和西红柿炒鸡蛋都可以吃那么他们有共同的结构吃,又比如饭店有做菜的功能,其他饭店也可以做菜,那么做菜这个方法可以作为抽象类来约束实例。抽象类(对有相同方法(结构)的实例进行约束),

抽象工厂模式:工厂模式里有约束当前实例的抽象类且有多个(1个以上的)(=。=不知道理解的对不对,希望有大佬可以下面评论教教我)

// 商业街 自己实现请用英文 请养成良好的编程习惯。(=。=)
class CommercialStreet{
    constructor(){
        this.西红柿炒鸡蛋 = new 西红柿炒鸡蛋()
        this.奶油蛋糕 = new 奶油蛋糕()
        this.西红柿炒鸡蛋.eat()
        this.奶油蛋糕.eat()
    }
}

class abstractEat{
    constructor(){
        if(new.target===eat){
            throw new Error('抽象类不能实例化!')
        }
    }
    eat(){
        throw new Error('抽象方法不能被调用!')
    }
}

// 家常菜
class 西红柿炒鸡蛋 extends abstractEat{
    constructor(){
        console.log('西红柿炒鸡蛋');
    }
    eat(){
        console.log('西红柿炒鸡蛋真好吃')
    }
}

// 蛋糕店
class 奶油蛋糕 extends abstractEat{
    constructor(){
        console.log('奶油蛋糕');
    }
    eat(){
        console.log('奶油蛋糕真香')
    }
}

工厂模式主要关注的是当前产品的创建,
    优势:良好的封装,符合开放封闭原则
    劣势: 增加系统复杂性
抽象工厂模式关注当前的抽象类,以及当前的产品。
    劣势:(违反了开放封闭原则,添加系统复杂性)
    优势:我们不需要知道产品的具体细节,只要看当前抽象类的方法(结构)进行编程就可以了

状态模式

一个对象有状态变化,每一个状态变化都会触发一个逻辑,我们不能总是if...else 来写,所以我们就把状态和当前对象分离开来,比如最常见都红绿灯。红灯状态下是(停下),黄灯状态是(警告),绿灯状态是(通行)。那么我们就可以把这3个状态和方法都抽离出来。提高代码复用,符合开放封闭原则

// 红绿灯例子 毕竟合适的状态管理例子
class RedLamp{
    constructor(){
        this.state = '红灯'
        console.log('我是红灯');
    }
    handle(){
        console.log('红灯停')
    }
}
class GreenLamp{
    constructor(){
        this.state = '绿灯'
        console.log('我是绿灯');
    }
    handle(){
        console.log('绿灯行')
    }
}
class YellowLamp{
    constructor(){
        this.state = '黄灯'
        console.log('我是黄灯');
    }
    handle(){
        console.log('黄灯警告')
    }
}

class Content{
    constructor(){
        this.state = null
        console.log('我是实体')
    }
    handleState(lamp){
        // 改变当前灯状态
        this.state = lamp.state
        // 执行当前灯的方法
        lamp.handle()
    }
}
// 实体
let con = new Content()
// 红灯实体
let Red = new RedLamp()
// 绿灯实体
let Green = new GreenLamp()
// 黄灯实体
let Yello = new YellowLamp()
// 当前红灯
con.handleState(Red)
// 黄灯
con.handleState(Yello)
// 绿灯
con.handleState(Green)

状态模式,
    优势:符合开放封闭原则,提搞代码可以维护性
    劣势: 添加了很多类,每个类都有自己的方法和状态,增加系统的负载
// pomise 高考必备 =。= 有需求的小伙子可以瞅一下
... 晚一会更新,今天工作太忙了。明天就给各位观众老哥,多写点,
有啥前端不理解的内容可以@小弟留言(小弟会按能力更新)。有啥特别想要了解的,小弟也可以多写几篇

单例模式

小时候我们打的游戏机了解一下,记录点就是一个单例模式的。单例模式就是不用重复去构建实例,直接取之前创建过的那个保存在内存中的实例,之前看 element 写的ui组件loading,也是单例模式。(个人意见不喜勿喷:elementjsx风格组件看起来没有iviewui好看,但是组件内部的代码感觉要健壮一点)(github也支持一下哈!你的点赞和关注是我写下去的动力)

// 最常见的单例模式
class SingleObject{
    login(){
        console.log('登录')
    }
}
// 自执行函数 恶汉式
SingleObject.getInstance = (function() {
    // 通过闭包来保存当前的 instance
    let instance = null;
    return function(params) {
        if (!instance){
            instance = new SingleObject()
        }
        return instance;
    }
})()

let SingleObject1 = SingleObject.getInstance()
let SingleObject2 = SingleObject.getInstance()
console.log(SingleObject1===SingleObject2)
// 懒汉式
class SingleObject{
    Instance
    login(){
        console.log('登录')
    }
    static getInstance(){
        if (!this.instance){
            this.instance = new SingleObject()
        }
        return this.instance;
    }
}

let SingleObject1 = SingleObject.getInstance()
let SingleObject2 = SingleObject.getInstance()
console.log(SingleObject1===SingleObject2)
懒汉式:和懒加载一样,使用得时候去创建实例子(实例复杂的时候可以使用。)
饿汉式:生命周期开始的时候就创建实例(实例不复杂的时候可以,复杂的时候影响页面加载速度)
单例模式,
    优势:只有一个实例,内存占用肯定少,提高性能
    劣势: 使用常见少,扩展性差,业务修改就得改,

代理模式

代理模式就是我们不直接访问原对象,通过访问代理对象访问原对象。比如你是一个导演,你肯定不会直接联系明星,而是联系明星的经纪人,由明星的经纪人来决定明星是否和你合作。

// 明星例子
class Star{
    constructor(name){
        this.name = name
    }
    sing(){
        console.log('我可以唱歌');
    }
}
class Agent{
    constructor(){
        console.log('我是经纪人');
    }
    sing(price,name){
        if(price>1000000){
            console.log('我们家宝宝有档期');
            let dehua = new Star(name)
            console.log(`我是${name}`)
            dehua.sing()
        }else{
            console.log('不好意思我们明星没有档期');
        }
    }
}
let agent = new Agent()
agent.sing(10000,'刘德华') // 不好意思我们明星没有档期
agent.sing(100000000,'刘德华') // 我是刘德华我可以唱歌

观察者模式(和订阅发布)

观察者模式就是比如你和小明都想和牛奶,然后拨打了牛奶站的电话。牛奶站的工作人员统一给你们进行配送这样的模式就是观察者模式,而订阅发布则是你给牛奶站的代理商打电话,不直接和牛奶站直接接触,牛奶站只需要给代理商送货就可以,而你只需要和代理商接触,这样牛奶站就结偶了不用处理送货之类的方法这样的方式就是订阅发布。

适配器模式

比如你去欧洲工作,他们的插线板你是不能直接使用的,需要在咱们的充电器外面加一个适配器然后就可以使用他们的插线板了,我们最常见的适配器模式就是当一个老接口和新接口同时使用,老接口可以 使用适配器去调用。这样我们的代码就更加容易的解耦。

外观模式

外观模式是最常见的模式了,我们也用的最多,当我们定义了一堆方法,提供统一的出口的。这样方式就是一个外观模式。我们前端和后端进行交互就是一个外观模式,服务器有统一的一个入口。

最后

能看到最后🙏谢谢大家了,多多点赞在github 上面对❤️是对我最好对鼓励,我会尽量分享一些自己使用得心得以及正确对食用方式

求靠谱内推(北京地区)可以留言我 +。=