关于前端设计模式

前端设计模式:

参考:

  • https://www.cnblogs.com/tugenhua0707/p/5198407.html
  • https://zhuanlan.zhihu.com/p/256405681
  • 设计模式6大原则:https://www.cnblogs.com/toutou/p/4870926.html
  • 前端面经:https://juejin.cn/post/6939774328858738696
  • 设计模式

https://juejin.cn/post/6844904125721772039#heading-33

关于前端设计模式_第1张图片

  • 什么是前端设计模式:设计模式是一套被反复使用的、多数人知晓、经过分类编目的优秀代码设计经验的总结。特定环境下特定问题的处理方法。
  • 作用:复用设计和代码,提高扩展性[面向接口编程,预留扩展插槽,新功能或特性很容易加入]
  • 核心思想:封装变化。
  • 最常用的模式有哪些啊。稍微了解的模式有哪些啊。

设计模式6大原则:为了升级扩展和维护的方便。降低依赖,降低耦合。

  • 开闭原则:观察代码整个逻辑的变与不变两部分。将变化与不变的部分分离。使变化的部分灵活可扩展,使不变的部分保持稳定。
  • 里氏代换原则:子类可以实现父类的抽象方法来扩展父类,不能覆盖父类的非抽象方法,即不能改变父类原有的功能。子类中可以增加自己特有的功能。
  • 单一职责:每个类都有自己要负责的职责。
  • 迪米特法则:一个对象尽量减少对其他对象的依赖,原则是低耦合,高内聚,提高代码复用率。
  • 接口隔离原则:使用多个隔离的接口比使用单个接口要好。降低依赖,降低耦合。
  • 依赖倒转原则:面向接口编程。依赖于抽象而不依赖于具体。开闭原则的基础。

GOF提出23种设计模式。

创建型模式:5

1.⭐原型模式:应用->设置函数的原型属性,实现继承。

原型有一个样板实例,原型模式简单理解就是拷贝实例对象。类初始化需要消耗很多资源,通过原型模式进行拷贝对象急减少了消耗。

    function Animal(name) {
        console.log('0000'+name);
        this.name = name || 'animal';
        this.eat = function(food) {
            console.log(`${this.name} 在吃${food}`)
        }
    }
    Animal.prototype.sleep = function() {
        console.log(this.name + '正在睡觉');
    }
    function Cat() {};//创建抽象类
    Cat.prototype = new Animal();//继承Animal类
    Cat.prototype.name = 'cat';
    var cat = new Cat();
    console.log(cat);
    cat.eat('鱼');
    cat.sleep();
    console.log(cat instanceof Cat);
    console.log(cat instanceof Animal);

2.⭐单例模式:应用->只允许被实例化一次。可以利用闭包。确保一个类只有一个实例

单例模式的应用:windows的任务管理器。不能同事打开两个任务管理器。支付功能,一个订单只能支付一次。
优缺点:

  • 节约系统资源。当需要频繁地创建和销毁对象时,可以提高系统的性能。
  • 扩展性不好,因为没有抽象层。
  • 单例类职责国中,在一定程度上违反了“单一职责”的原则
  • 单例模式不适用于变化的对象,因为无法保存状态。
  • 滥用单例模式会带来一些负面影响:比如如果实例化的对象长时间没有被引用,系统当成垃圾回收,将导致对象状态的丢失。
let singleCase = function(name){
    this.name = name;
};
// 获取实例对象
let getInstance = (function() {
    var instance = null;
    return function(name) {//返回的这个函数引用了instance。所以导致IIFE立即执行函数执行完被销毁之后,变量instance会被缓存起来,形成了闭包。所以当传入two参数再次执行时,不会重新声明一个变量instance而是去访问缓存的instance。
        if(!instance) {//相当于一个一次性阀门,只能实例化一次
            instance = new singleCase(name);
            console.log(instance);
        }
        return instance;
    }
})();//
// 测试单体模式的实例,所以one===two
let one = getInstance("one");
let two = getInstance("two"); 

3.⭐工厂模式:应用->创建实例。

简单工厂 :用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)

直接使用工厂方法就能创建对象,而不用使用new关键字。在创建对象的过程中,看不到构造函数实例化的过程。
实例化对象的最佳方式。通过使用一个共同的接口来指向新创建的对象。

var Factory=function(type,content){
    if(this instanceof Factory){//判断是不是实例对象,是就返回相关属性。不是就new创建。
        return new this[type](content);
    }else{
      return new Factory(type,content);
    }
}
Factory.prototype={
  Python:function(content){
    console.log('Python值为',content);
  }
}
Factory('Python','我是Python');

function playerFactory (username){
    var user= new object ();
    user .username = username;
    return user ;
}
var xm = playerFactory( 'xiao ming ')
3.1.⭐简单工厂模式/静态工厂模式:应用->抽取类相同的属性和方法。创建单一对象

创建一个函数,通过接收参数来生成不同的对象。
提供一个[创建一系列相关或相互依赖对象的]接口。

    // let useFactory = function(role) {
    //     function User(opt) {
    //         this.name = opt.name;
    //         this.age = opt.age;
    //     }
    //     switch(role) {
    //         case 'administrator':
    //             return new User(administrator);
    //             break;
    //         case 'admin':
    //             return new User(admin);
    //             break; 
    //         default:
    //            console.log('不是管理员')
    //     }
    // }
    // let administrator = UserFactory('administrator');
    // let admin = UserFactory('admin');

function UserFactory(role) {
    function User(opt) {
      this.name = opt.name;
      this.age = opt.age;
      console.log(this)
    }
    switch (role.name) {
      case 'superAdmin':
        return new User(role);
        break;
      case 'admin':
        return new User(role);
        break;
      case 'user':
        return new User(role);
        break;
      default:
        console.log('000')
    }
}
//调用
let superAdmin = UserFactory({name:'superAdmin',age:24});
let admin = UserFactory({name:'admin',age:28});
let normalUser = UserFactory({name:'user',age:18});
3.3.抽象工厂模式:将成员对象的实例化推迟到子类中。父类是一个抽象类,具体的业务逻辑在子类中实现。创建多类对象
//子类继承父类,子类重写父类中的抽象方法。

5.建造者模式:


结构型模式:7

1.⭐适配器模式:将一个接口转换成客户端需要的接口而不需要去修改客户端代码。使得不兼容的代码可以一起工作。

应用于适配函数参数。
举个例子:存在3条数据,分别来自3个数据来源,是3种不同的数据结构的。而我们接收的数据格式是规定好的某一种。采用适配器模式,就可以实现兼容,将不同的数据结构都适配成我们所能接收的数据结构。

//鸭子
var Duck = function(){};
Duck.prototype.fly = function(){
throw new Error("该方法必须被重写!");
};
Duck.prototype.quack = function(){
throw new Error("该方法必须被重写!");
}
//火鸡
var Turkey = function(){};
Turkey.prototype.fly = function(){
    throw new Error(" 该方法必须被重写 !");
};
Turkey.prototype.gobble = function(){
    throw new Error(" 该方法必须被重写 !");
};
//然后再定义具体的鸭子和火鸡的构造函数,分别为:
//鸭子
var MallardDuck = function () {
    Duck.apply(this);
};
MallardDuck.prototype = new Duck(); //原型是Duck
MallardDuck.prototype.fly = function () {
    console.log("可以飞很长的距离!");
};
MallardDuck.prototype.quack = function () {
    console.log("嘎嘎!嘎嘎!");
};
//火鸡
var WildTurkey = function () {
    Turkey.apply(this);
};
WildTurkey.prototype = new Turkey(); //原型是Turkey
WildTurkey.prototype.fly = function () {
    console.log("飞翔的距离貌似有点短!");
};
WildTurkey.prototype.gobble = function () {
    console.log("咯咯!咯咯!");
};
//为了让火鸡也支持quack方法,我们创建了一个新的火鸡适配器TurkeyAdapter:
var TurkeyAdapter = function(oTurkey){
    Duck.apply(this);
    this.oTurkey = oTurkey;
};
TurkeyAdapter.prototype = new Duck();
TurkeyAdapter.prototype.quack = function(){
    this.oTurkey.gobble();
};
TurkeyAdapter.prototype.fly = function(){
    var nFly = 0;
    var nLenFly = 5;
    for(; nFly < nLenFly;){
        this.oTurkey.fly();
        nFly = nFly + 1;
    }
};

2.⭐装饰器模式:不改变原对象的基础上,给对象添加属性或方法

let decorator=function(input,fn){
  //获取事件源
  let input=document.getElementById(input);
  console.log(input.onclick)
  //若事件源已经绑定事件
  if(typeof input.onclick=='function'){
    //缓存事件源原有的回调函数
    let oldClickFn=input.onclick;
    //为事件源定义新事件
    input.onclick=function(){
      //事件源原有回调函数
      oldClickFn();
      //执行事件源新增回调函数
      fn();
    }
  }else{
    //未绑定绑定
    input.onclick=fn;
  }
}
//测试用例
decorator('textInp',function(){
  console.log('文本框执行啦');
})
decorator('btn',function(){
  console.log('按钮执行啦');
})

3.外观模式:为子系统中的一组接口提供一个一致的界面,简化复杂接口

//在形式上,外观模式在javascript中就像这样:
function a(x){
   // do something
}
function b(y){
   // do something
}
function ab( x, y ){
    a(x);
    b(y);
}

4.桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。事件监控 ?


5.组合模式

6.享元模式

7.⭐代理模式:不直接操作原有对象,而是委托代理者去进行。代理的作用:对我们的请i去预先进行处理,或者转接给实际对象去处理。

代理模式是通过代理来控制对对象的访问。实际功能还是对象本身去执行。代理只是控制是否去执行这个功能。
代理模式使用场景:

  • 模块职责单一且可复用
  • 两个模块间的交互需要一定限制关系

行为型模式:11 关注对象之间的相互交互,解决系统在运行时对象之间的相互通信和协作,进一步明确对象的职责。

1.⭐模版方法模式:使用原型链。在父类中包括实现一些公共方法及封装子类中所有方法的执行顺序,在子类中可以重写方法。定义一个模块供以后传不同的参数调用。基于继承。抽象父类,在父类中封装子类的算法框架。具体实现的子类

let myFather = function(){};//创建抽象类父类。在父类中封装子类的算法框架
myFather.ptototype.one = function() {
    console.log('把水煮沸')
}
myFather.ptototype.two = function() {
    throw new Error("子类必须重写方法");
}
myFather.ptototype.three = function() {
    throw new Error("子类必须重写方法");
}
myFather.prototype.isdo = function() {//添加钩子方法给不一定执行的函数添加条件控制
    return true;
}
myFather.prototype.init = function() {
    this.one();
    this.two();
    if(this.isdo()) {//3是否执行要看isdo的返回值
        this.three();
    }
}
let mySon = function(){};//创建子类
mySon.prototype = new myFather();//继承抽象类
mySon.prototype.two = function() {
    console.log('重写two方法')
}
mySon.prototype.isdo = function() {
    return window.confirm("需要执行three方法吗");
}
mySon.prototype.three = function() {
    console.log('重写three方法')
}
let mySon_duixiang = new mySon();
mySon_duixiang.init();

2.⭐观察者模式/发布-订阅模式:解决类与对象,对象与对象之间的耦合/依赖。发布者把消息推送给订阅者。发布,监听,处理

  • 模块之间存在[一对多]的依赖关系。让多个观察者对象同时监听某个主题对象,当对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
  • 优缺点:
    各模块相互独立。
    依赖模块不稳定,依赖关系不稳定。
    各模块由不同的人员,团队开发。
  • 广泛应用于异步编程。DOM事件。

3.状态模式:定义一个对象状态改变会改变的行为变化,解决复杂的if判断


4.⭐策略模式:定义了一系列家族算法,并把每一种算法单独封装起来,让算法之间可以相互替换。简化if…else…所带来的复杂性维护难度。

策略+组合:
算法可以自有切换,扩展性很好,避免了使用多重条件判断。
所有策略都需要对外暴露

    let strategies = {
        'a': function(val) {
            return val * 4;
        },
        'b': function(val) {
            return val * 3;
        },
        'c': function(val) {
            return val * 2;
        }
    }
    let calcu = function(level,val) {
        return strategies[level](val);
    }
    calcu('a',777);

5.访问模式:通过继承封装一些该数据类型不具备的属性,让对象具备数组的操作方法

6.中介者模式:设置一个中间层,处理对象之间的交互

7. ⭐责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

作用:解耦了各节点关系。各节点灵活拆分重组。
使用场景:

  • 负责的是一个完整流程或流程种的某个环节。
  • 各环节可以复用
  • 各环节有一定的执行顺序
  • 各环节可重组

委托模式 ,节流模式

事件代理/事件委托:把原本需要绑定到子元素的事件委托给父元素。让父元素承担事件监听的工作。原理是DOM元素的事件冒泡。

好处:减少事件数量,避免内存泄漏,有利于提高性能。

重绘和节流:

singleton:单例模式,用来减少重复创建对象。

factory:工厂模式,用来解耦。

iterator:迭代器模式,用来遍历对象。

observer:观察者模式,用来收发消息。

templete:模板模式,用来避免执行相同的操作。

strategy:策略模式,用来定义算法等。

你可能感兴趣的:(前端设计模式,前端设计模式)