Javascript 设计模式系统讲解与应用

180 阅读1分钟

一、单例模式

应用:JQuery中的$,登录框,购物车,vuex,redux等

1.创建登录框

class LoginForm {
    constructor() {
        this.state = 'hide'
    }
    show() {
        if (this.state === 'show') {
            alert('已经显示')
            return
        }
        this.state = 'show'
        console.log('登录框已显示')
    }
    hide() {
        if (this.state === 'hide') {
            alert('已经隐藏')
            return
        }
        this.state = 'hide'
        console.log('登录框已隐藏')
    }
}

LoginForm.getInstance = (function () {
    let instance
    return function () {
        if (!instance) {
            instance = new LoginForm();
        }
        return instance
    }
})()

// 一个页面中调用登录框
let login1 = LoginForm.getInstance()
login1.show()
// login1.hide()
// 另一个页面中调用登录框
let login2 = LoginForm.getInstance()
login2.show()// 两者是否相等
console.log('login1 === login2', login1 === login2)

二、适配器模式

1、vue中的computed实际上也是适配器模式的一种应用,比如data里面的数据是hello,而模版需要的是一个倒叙的字符串,就可以在computed中返回return XXX.split('').reverse().join('')。

三、装饰器模式

es7已原生支持装饰器

日常开发中可以参考这个库core-decorators

装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

四、代理模式

事件委托

经纪人代理明星

// 明星
let star = {
    name: '张XX',
    age: 25,
    phone: '13910733521'
}

// 经纪人
let agent = new Proxy(star, {
    get: function (target, key) {
        if (key === 'phone') {
            // 返回经纪人自己的手机号
            return '18611112222'
        }
        if (key === 'price') {
            // 明星不报价,经纪人报价
            return 120000
        }
        return target[key]
    },
    set: function (target, key, val) {
        if (key === 'customPrice') {
            if (val < 100000) {
                // 最低 10w
                throw new Error('价格太低')
            } else {
                target[key] = val
                return true
            }
        }
    }
})

// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)

// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 150000 
// agent.customPrice = 90000  // 报错:价格太低
console.log('customPrice', agent.customPrice)

五、外观模式

高层接口集成低层接口(洗衣机可以一键开洗也可以单独选择漂洗、干洗等,一键开洗也就集成了一些低层接口)

外观模式不符合单一职责原则和开放封闭原则,因此谨慎使用不可滥用。

六、观察者模式

简单实现

// 主题,接收状态变化,触发每个观察者
class Subject {
    constructor() {
        this.state = 0
        this.observers = []
    }
    getState() {
        return this.state
    }
    setState(state) {
        this.state = state
        this.notifyAllObservers()
    }
    attach(observer) {
        this.observers.push(observer)
    }
    notifyAllObservers() {
        this.observers.forEach(observer => {
            observer.update()
        })
    }
}

// 观察者,等待被触发
class Observer {
    constructor(name, subject) {
        this.name = name
        this.subject = subject
        this.subject.attach(this)
    }
    update() {
        console.log(`${this.name} update, state: ${this.subject.getState()}`)
    }
}

// 测试代码
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)
s.setState(2)
s.setState(3)

应用场景:

(1)nodejs中:自定义事件,处理http请求(例如'data', 'end'事件),多进程通信

(2)vue和React组件生命周期触发

(3)vue watch

7.迭代器模式

实现一个简单的迭代器,支持遍历数组和类数组

class Iterator {
  constructor(conatiner) {
      this.list = conatiner.list
      this.index = 0
  }
  next() {
      if (this.hasNext()) {
          return this.list[this.index++]
      }
      return null
  }
  hasNext() {
      if (this.index >= this.list.length) {
          return false
      }
      return true
  }
}

class Container {
  constructor(list) {
      this.list = list
  }
  getIterator() {
      return new Iterator(this)
  }
}

// 测试代码
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
  console.log(iterator.next())
}

可迭代对象(以数组为例)原型上内置的迭代器

8、职责链模式

js中的promise.then方法,jq中的链式操作,nodejs的流

9、命令模式

js中的运用:富文本编辑器(浏览器封装了一个命令对象,例如document.execCommand('bold'),document.execCommand('undo'))

参考资料:Javascript设计模式系统讲解与应用