最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基础进阶-那些开发中常用的设计模式

    正文概述 掘金(_光光)   2021-02-17   513

    设计模式:就是将常用的代码套路,归纳总结后系统的表达出来。
    鉴于js语言特性以及前端实际开发的应用场景,笔者只在此总结几个经典的设计模式。

    #工厂模式

    工厂模式是用来创建对象最常用的设计模式,它不暴露创建对象的具体逻辑;而是将所有逻辑封装在函数中,通过这个函数批量化的创建对象。

    哪些场景适合工厂模式?

    • 对象的构建十分复杂
    • 需要依赖具体环境创建不同实例
    • 处理大量具有相同属性的小对象

    示例1

    我们需要一个可以生成用户的构造函数,这个用户的实例可能是普通用户、商户、管理员。每种角色都有对应权限,只可访问有权限的页面。

      function createUser(type) {
          function User(option) {
            this.name = option.name
            this.viewPage = option.viewPage
          }
          switch (type) {
            case "user":
              return new User({ name: "user", viewPage: ["主页", "设置页"] })
              break;
            case "merchant":
              return new User({ name: "merchant", viewPage: ["主页", "设置页", "XXX页"] })
              break;
            case "admin":
              return new User({ name: "admin", viewPage: ["主页", "设置页", "XXX页", "XXXX页"] })
              break;
            default:
              throw new Error("参数错误")
              break;
          }
        }
        console.log(createUser("user"));
    

    示例二

    function createUser(role) {
    	return new createUser.prototype[role]()
    }
    createUser.prototype.user = function () {
    	this.name = "user";
        this.viewPage = ["主页", "设置页"]; 
    }
    createUser.prototype.admin = function () {
    	this.name = "merchant";
        this.viewPage = ["主页", "设置页", "XXX页"]; 
    }
    createUser.prototype.merchant = function () {
    	this.name = "merchant";
        this.viewPage = ["主页", "设置页", "XXX页", "XXXX页"]; 
    }
    

    #单例模式

    单例模式可以确保一个特定的类最多只能存在一个实例,这意味着你第二次使用相同的类创建实例对象时,应该得到和第一次创建相同的对象,它的实现原理往往是通过闭包。

    哪些场景适合单例模式?

    • 全局只需要唯一对象的,例如、弹窗、遮罩。包括ES6模块化和CommonJS模块化导出的对象也是唯一对象。

    示例一

    var singleUser = (function () {
        function User (name) {
        	this.name = name;
        }
        var instance = null
        if(instance) return instance
        return instance = new User(Name)
    })();
    

    示例二

    通过传入新的构造函数得到新的生成单例对象的构造函数  
    
    var singleUser = function (fn) {
        var instance = null
        return function (args) {
        	if(instance) return instance
       	return instance = new fn(args)
        }
    })
    

    #适配器模式

    适配器模式是将一个类(对象)的接口(方法或属性)转换成另外一个符合我们使用规范的类(对象)。它可已经将一些不兼容的接口放在一起正常的工作

    哪些场景适合适配器模式

    • 当我们需要对接口的提供者喝消费者进行兼容的时候,对旧代码的渐进式的改造以及对某些已有老接口的改造。

    示例一

    当前场景中,有一个110伏电压、三口插头的电源插座,而我们的设备需要的电源为220伏电压、两口插座。所以我们需要一个适配器帮我们完成适配

    class Power{
    	constructor() {
        	this.serverVoltage = 100
            this.serverShape = "triangle"
        }
    }
    class Device{
    	constructor() {
        	this.consumerVoletage = 220
            this.consumerShape = "douple"
        }
        usePower(power){
        	if(power) throw new Error("请接入电源")
            if(power.serverVoltage!==220||power.serverShape!=="douple) throw new Error("电源不符合规范“)
        }
    }
    class Adapter { 
    	constructor() {
        	this.serverVoltage = 220
            this.serverShape = "douple"
        }
    	usePower(power){
        	if(power) throw new Error("请接入电源")
            if(power.serverVoltage!==100||power.serverShape!=="triangle) throw new Error("电源不符合规范“)
            this.consumerVoletage = 100
            this.consumerShape = "triangle"  
        }
    }
    var myPower = new Power()
    var myDevice = new Device()
    var myAdapter = new Adapter()
    myAdapter.usePwer(myPower)
    myDevice.usePower(myAdapter)
    

    #装饰器模式

    装饰器模式是指向现有的对象添加新的新的功能,同时又不改变其结构。使多个不同的类或对象之间共享或拓展一些方法或行为。

    哪些场景适合适配器模式

    • 装饰器可以极大的提高我们的开发效率,通过对非业务逻辑代码的封装快速完成可复用工作,例如React的高阶组件、Vue的TS语法,但是装饰器不要滥用 否则会让我们的代码混乱。

    装饰器语法

    • 饰器装饰类时,装饰器传入的参数仅有一个,参数为被装饰的类的实例
    • 饰器装饰类实例的方法时,会获取三个参数:target(类的原型对象)、name(被装饰的方法名)、descriptor(属性描述符);属性描述符:value(属性值)、enumberable(是否可配置)、configurable(是否可配置)、writable(是否可重写)

    示例一

    使用js装饰器需要通过babel将这个高语语法进行转义变成浏览器能够识别的es2015语法

    新建项目文件
    /src、/src/index.html、/src/index.js

    /src/index.js

    function addSayHi(target){
      target.prototype.sayHi = function() {console.log("你好方法被调用“)}
    }
    @addSayHi
    class Person {
      constructor(name){
        this.name = ame
      }
    }
    var zs = new Person("张三")
    zs.sayHi()
    

    安装babel依赖
    npm install --save-dev @babel/core @babel/cli @babel/preset-env
    npm install --save @babel/polyfill

    安装解析装饰器相关的插件
    npm install --save-dev @babel/plugin-proposal-decorators
    npm install --save-dev @babel/plugin-proposal-class-properties

    配置babel
    在项目根目录新建babel.config.json

    {
      "presets": [
        [
          "@babel/env",
          {
            "targets": {
              "edge": "17",
              "firefox": "60",
              "chrome": "67",
              "safari": "11.1",
            },
            "useBuiltIns": "usage",
            "corejs": "3.6.5",
          }
        ]
      ],
      //  用来解析装饰器和Class语法的插件
      "plugins": [
        ["@babel/plugin-proposal-decorators", { "legacy": true }],
        ["@babel/plugin-proposal-class-properties", { "loose" : true }]
      ]
    }
    

    配置npm指令

    "script":{
    	"build":"babel index.js -o build.js"    //将index.js解析编译到build.js中
    }
    

    示例二

    function useTool(target,name,descriptor){
    	let oldValue = descriptor
        descriptor.value = function() {
        	console.log("这是新方法")
            return oldValue.apply(this,arguments)
        }
    }
    class Tool {
      constructor() {}
      add(a,b){
      	return a+b
      }
    }
    

    #代理模式

    代理模式为一个对象提供另外一个对象,通过这个代理对象来控制对这个对象的访问。

    proxy对象

    proxy可以实现对一个对象的代理功能,proxy构造函数需要传入两个参数:target(需要被代理的源对象),handle(代理配置对象);handle具有两个拦截器方法,set(修改源对象属性时会被触发),get(访问源对象属性时会被触发)。set和get拦截器的都有两个形参:target(当前操作的源对象),key(当前操作的属性名)

    哪些场景适合代理模式?

    • 调用对象需要被保护,我们通过代理对象来完成程序的逻辑操作。将代理对象与调用对象分离开降低系统上的耦合度。

    示例1

    var gege = {
      name:"哥哥",
      isHandsome:true
    }
    var proxy= new Proxy(gege,{
      get(target,key) {
        if(key=="isHandsome"&&!target[key]) {
           console.log("不,我们的哥哥很帅")
           return true
        }
        return target[key]
      },
      set(target,key,value) {
        if(key=="isHandsome"&&!value){
          console.log("修改成功,但是哥哥还是最帅的")
        }
          target[key] = value
       }
    })
    

    代理模式、装饰器模式、适配器模式的区别?

    适配器模式提供了不同的新接口,通常用作接口的兼容处理 代理模式提供了一模一样上的接口,对行为进行拦截 装饰器模式直接访问原接口,对原接口进行改造。

    #外观模式

    外观模式就是对不同接口的封装,实现了所有接口的兼容处理。例如DOM绑定事件的兼容处理。

    示例1

    function addEvent(DOM,type,handle) {
      if(DOM.addEventListener) return DOM.addEventListener(type,handle,false)
      if(DOM.attachEvent) return DOM.attachEvent("on"+type,handle)
      DOM["on"+type] = handle 
    }
    

    #观察者模式

    观察者模式定义了对象与对象间的一种依赖关系,只要当一个对象的状态发生变化时,所有依赖它的对象会得到通知并被自动更新。

    示例1

    //  创建发布者的类
    class Publisher{
        // 实例的私有状态
        _state = null
        // 该发布者实例的订阅者列表
        substribers = null
        get state() {
          return this._state
        }
        // 当时state状态更新时,调用notify通知所有订阅者
        set state (value) {
          this._state = value
          this.notify({ key:"state",value})
        }
        notify(newState) {
          if(!this.substribers) return
          this.substribers.forEach(substriber =>substriber.update(newState));
        }
        // collect用来添加订阅者
        collect(substriber){
          if(!this.substribers) return this.substribers = [substriber]
          this.substribers.push(substriber)
        }
      }
      let id = 0
      //  创建订阅者的类
      class Substriber{
        publishers = null
        subId = ++id
        // 创建订阅者时需要传入被订阅的发布者
        constructor(publisher) {
          this.subscribe(publisher)
        }
        // subscribe用来添加订阅
        subscribe(publisher) {
          publisher.collect(this)
          if(!this.publishers) return this.publishers = [publisher]
          this.publishers.push(publisher)
        }
        // 订阅对象的状态被更改时执行的逻辑
        update(data) {
          console.log('发布新动态'+this.subId+"-接收到新属性",data);
        }
      }
    

    #迭代器模式

    迭代器模式 是指按照一中方法顺序的访问同一个聚合对象的各个元素,不用关心对象的内部结构,也可以按顺序访问各个元素。
    迭代器分为内部迭代器和外部迭代器

    内部迭代器示例

    let arr = [1,2,3,4,5,6,7]
    let each = function(arr,callback) {
    	for(let index = 0;index<arr.length;index++){
          callback.call(null,i,arr[i])
        }
    }
    each(arr,function(i,value) {console.log("当前索引"+i+"当前值"+value)})
    

    外部迭代器示例

    class Iterator{
      constructor(list) {
      	this.list = list
        this.index = 0
      }
      next() {
        // 每次迭代器调用next方法返回最新的值和 迭代完成的状态
        return {
          value:this.list[this.index++],
          done:this.index > this.list.length-1
        }
      }
    }
    var myIterator = new Iterator([1,2,3,,4])
    myIterator.next()    
    

    可迭代对象

    ES6提供了迭代器接口,一些对象是默认可以使用的。例如:Array、Set、函数的arguments、NodeList、String。 以上对象都具备Symbol.iterator属性,调用该属性会返回外部迭代器对象,通过调用这个对象的next方法会迭代到下一步。
    以上可迭代对象可以使用for...of迭代对象

    Generator函数

    Generator函数是ES6提供生产可迭代对象的函数,通过它可以实现同步代码写异步的逻辑。 generator生成器:他必须使用function* 来声明,函数的内部的必须使用yield来声明迭代的进度

    function* foo(x) {
        yield x + 1;
        yield x + 2;
        return x + 3;
    }
    var res1 = foo(1)
    var res2 = res1.next()
    

    #状态模式

    状态模式是将将主体对象和状态对象分离开,根据主体对象内部的状态分别委托不同的状态对象,每个状态对象都有不同的逻辑。

    哪些场景适合状态模式?

    • 一个对象的行为取决于它的状态,并且他能在运行的时刻改变的他的行为
    • 一个操作中含有大量的if-else语句,并且这些分支语句都依赖于对象的状态

    非状态模式

    非状态模式 只能通过if判断不同的状态并据此执行不同逻辑。如果我们中间插入新的状态可能整个程序代码都有影响。

    class Clame {
        this.state = "off"
        pressButton () {
        	if(this.state === "off") {
              console.log("切换到弱光模式")
              this.state = "weakLight"
            } else if(this.state === "weakLight") {
              console.log("切换到强光模式")
              this.state = "strongLight"
            } else if(this.state === "strongLight") {
              console.log("切换到关闭模式")
              this.state = "off"
            }
        }
    }
    

    状态模式

    class Lamp {
      this.state =  FSM.offLight 
      pressButton() {
      	this.state.trigger.call(this)
      }
    }
    /* FSM:有限状态机
     *   1、状态的总数是有限的
     *   2、任意时刻只会处于一个状态
     *   3、在某些情况下会从一种状态变为另外一种状态
    
    var FSM = {
      offLight:{
        trigger() {
          console.log("切换到弱光模式")
          this.state = FSM.weakLight
        }
      },
      weakLight:{
        trigger() {
          console.log("切换到强光模式")
          this.state = FSM.strongLight
        }
      },
      strongLight:{
        trigger() {
          console.log("切换到关灯模式")
          this.state = FSM.offLight
        }
      },
    }
    

    #享元模式

    享元模式是将对象的公共部分抽离出来并做缓存处理,当创建大量对象可以节省内存开销。

    哪些场景适合享元模式?

    • 页面中的表格,如果存在大量表格DOM,可以将他们抽离出来看上去是在滚动实际只改变当前单元格的内容。

    示例

    // IPHONE示例除了SN每个都是唯一的以外,型号、屏幕尺寸、内存是公共的部分
    function Iphone (model,screen,memory,SN) {
    	this.flyWeight = flyWeightFactory.get(model,screen,memory)
        this.SN = SN
    }
    function IphoneFlyWeight(model,screen,memory) {
      this.model = model
      this.screen = screen
      this.memory = memory
    }
    
    var flyWeightFactory = ()(
      //  通过闭包的函数来做一个函数处理
      var = iphones = {}
      return {
       get(model,screen,memory) {
         var key = model+screen+memory
         if(key) return iphones[key] = new IphoneFlyWeight(model,screen,memory)
         return iphones[key]
       }
      }
    );
    

    #备忘录模式

    备忘录模式就是将上次操作的数据保存下来,一旦撤销到上次状态就把上次的数据覆盖回来。

    示例

    Vuex的浏览器"时光旅行"功能。

    #中介者模式

    中介者模式是封装的一层调度器,用来处理多对多的映射关系。例如MVC的controller


    起源地下载网 » 基础进阶-那些开发中常用的设计模式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元