js 几种常用的设计模式

426 阅读4分钟

这是我参与更文挑战的第11天,活动详情查看: 更文挑战

写在前面

这几天在学一下设计模式,虽然说前端是写页面,做切图仔(调侃一下),但是也有很大部分时候是写逻辑的,下面我把常用的设计模式总结一下,正所谓工欲善其事必先利其器, 如果我们能在代码中使用到一些设计模式,对我们代码的优化还是有很大帮助的。

设计模式

发布-订阅模式

发布-订阅模式其实是1对多的关系,订阅者先把事件订阅到调度中心,然后发布者需要执行哪个事件,就把该事件的名称发到调度中心,调度中心则调用该事件对应的函数。 vue2.0的内部很多实现就是利用这个模式

// 订阅-发布模式-简易版
class Publish {
  constructor () {
    // 调度中心
    this.subObj = {}
  }
  // 订阅者
  on (name, fn) {
    if (this.subObj[name]) {
      this.subObj[name].push(fn)
    } else this.subObj[name] = [fn]
  }
  // 发布者
  emit (name, data) {
    if (this.subObj[name]) {
      for (let i = 0; i < this.subObj[name].length; i++) {
        this.subObj[name][i](data)
      }
    }
  }
  // 取消订阅
  off (name, fn) {
    if (this.subObj[name]) {
      if (fn) {
        for (let i = 0; i < this.subObj[name].length; i++) {
          if (this.subObj[name][i] === fn) {
            this.subObj[name].splice(i, 1)
          }
        }
      } else {
        delete this.subObj[name]
      }
    }
  }
}


const a = new Publish()
const fn1 = () => {
 console.log('fn1')
}
const fn2 = () => {
 console.log('fn2')
}
a.on('aaa', fn1) // 订阅 fn1
a.on('aaa', fn2) // 订阅 fn2
a.emit('aaa') // 发布,输出 fn1 和 fn2

a.off('aaa', fn1) // 取消订阅
a.emit('aaa') // 只输出 fn2

发布-订阅模式中发布者和订阅者是没有关联的,完全是调度中心去管理,这样处理,灵活度高,易扩展;

观察者模式

观察者模式和发布-订阅模式很像,但是区别是它没有调度中心,他是主要2种角色,目标对象观察者,观察者把自己的函数方法添加到目标对象里,当目标对象触发的时候,依次执行所有观察者对应的方法。

// 观察者模式
// 目标对象
class Target {
  constructor () {
    this.observers = []
  }
  add (obverser) {
    this.observers.push(obverser)
  }
  notify () {
    for (let i = 0; i < this.observers.length; i++) {
      this.observers[i].update()
    }
  }
}
// 观察者
class Observer {
  constructor () {
  }
  update () {
    console.log('我被调用了')
  }
}
// 目标对象
const target = new Target()
// 观察者1
const observer1 = new Observer()
// 观察者2
const observer2 = new Observer()

target.add(observer1)
target.add(observer2)
// 通知
target.notify() 
// 我被调用了 
// 我被调用了

观察者模式没有有发布-订阅那么灵活,它需要目标对象和观察者耦合在一起,而且目标对象不知道哪个观察者是哪个,触发的时候只能通知所有观察者。

单体模式

单体模式,就是指一个类内部只实例化一个实例,不管你使用这个类创建几个实例,这几个实例返回的都是同一个实例。

class singleMode {
    constructor(name) {
        this.name = name
    }
    getName () {
       return this.name
    }
    getInstance () {
       return this
    }
}
createSingleMode = (function () {
  let singleModeInstance
  return function (name) {
   if (!singleModeInstance) singleModeInstance = new singleMode(name)
   return singleModeInstance
  }
})()
const singleMode1 = new createSingleMode('答案cp3')
const singleMode2 = new createSingleMode('答案cp3')
console.log(singleMode1 === singleMode2) // true
console.log(singleMode1.getInstance() === singleMode2.getInstance()) // true
console.log(singleMode1.getName()) // 答案cp3
console.log(singleMode2.getName()) // 答案cp3

从以上代码可以看到,如果创建过实例,则不需再创建,直接返回,所以返回的实例是相等的,是同一个实例。

工厂模式

工厂模式,在内部将创建具体对象的过程操作单独封装,不对外暴露出来。

function Factory (name) {
   const obj = {}
   obj.name = name
   obj.getName = function () {
    return this.name
   }
   return obj
}
const factory1 = new Factory('答案')
const factory2 = new Factory('cp3')
console.log(factory1.getName()) // 答案
console.log(factory2.getName()) // cp3

上面代码中obj就是在函数Factory内部创建,并且赋值方法,属性等。

策略模式

定义好需要的策略,然后根据传入的策略名去执行对应的策略。 比如你想开通会员,有青铜会员白银会员黄金会员,开通会员后对应的商品折扣是不同的,这时候就可以用上策略模式。

const memberType = {
  copper: function (price) {
    return  price * 0.9
  },
  silver: function (price) {
    return  price * 0.7
  },
  gold: function (price) {
    return price * 0.5
  }
}
memberType['copper'](100) // 打折后 90
memberType['silver'](100) // 打折后 70
memberType['gold'](100)   // 打折后 50

根据身份定好折扣,传入身份和价格,就知道打折后是多少,这种策略模式可读性强,维护性好,不需要写多余的ifelse

代理模式

给目标对象设置一个代理,不直接调用目标对象。

// 为这个target设置一个代理
class Target {
   constructor (name) {
    this.name = name
   }
   getName () {
    return this.name
  }
}
// 代理
class ProxyTarget {
  constructor (name) {
      this.target = new Target(name)
  }
  getName () {
    return this.target.getName()
  }
}
const proxy = new ProxyTarget('答案cp3')
proxy.getName() // 答案cp3

以上就是给Target对象设置一个ProxyTarget,外界不能直接访问到Target

装饰器模式

装饰器模式就是在不改变对象自身的基础上,给对象添加新方法,同时旧方法也还在

class Circle {
    draw() {
        console.log('先画一个圆形')
    }
}
// 装饰器
class DecoratorMode {
    constructor(circle) {
        this.circle = circle
    }
    draw() {
        this.circle.draw()
        this.setRedBg(this.circle)
    }
    setRedBg(circle) {
        console.log('再设置红色背景')
    }
}

let circle = new Circle()
let decorator = new DecoratorMode(circle); // 把circle实例传入
decorator.draw();  // 先画一个圆形,再设置红色背景

上面代码中,装饰器模式在对象的draw方法额外加上了setRedBg方法

总结

以上就是最近学到的几种常用的设计模式的总结,希望对你们能有帮助。一起加油~