Skip to content

设计模式

观察者模式

两个角色

js
class Subject {
  constructor(name) {
    this.name = name
    this.observers = []
  }

  add(observer) {
    this.observers.push(observer)
  }

  notify() {
    this.observers.forEach(observer => {
      observer.update(this.name)
    })
  }
}

class Observer {
  constructor(name) {
    this.name = name
  }

  update(...args) {
    console.log(`${args} updated, ${this.name}`)
  }
}

const tv = new Subject("三体")
const you = new Observer("tom")
const me = new Observer("jack")

tv.add(you)
tv.add(me)

tv.notify()

发布订阅模式

三个角色:发布者、调度中心、订阅者

js
// 发布者
class Publisher {
  constructor(name) {
    this.name = name
    this.eventChannel = null
  }

  connect(eventChannel) {
    this.eventChannel = eventChannel
  }

  notify(type, ...args) {
    this.eventChannel.notify(type, ...args)
  }
}

// 通知者
class EventChannel {
  constructor(name) {
    this.name = name
    this.subscribers = {}
  }

  subscribe(type, cb) {
    if (!this.subscribers[type]) this.subscribers[type] = []
    this.subscribers[type].push(cb)
  }

  unsubscribe(type, cb) {
    const cbs = this.subscribers[type]
    if (cbs && cbs.length) {
      cbs.splice(cbs.findIndex(i => i === cb), 1)
      console.log('取消订阅成功')
    }
  }

  notify(type, ...args) {
    const cbs = this.subscribers[type]
    if (cbs && cbs.length) {
      cbs.forEach(element => {
        element.update(type, ...args)
      })
    } else {
      console.log("该事件无订阅者")
    }
  }
}

// 订阅者
class Subscriber {
  constructor(name) {
    this.name = name
  }

  update(type, ...args) {
    console.log(`${this.name} 收到了 ${type}, ${args}`)
  }
}

const pub = new Publisher("任务中心")
const ec = new EventChannel("事件中心")
pub.connect(ec)

const user1 = new Subscriber("tom")
const user2 = new Subscriber("jack")
ec.subscribe("warTask", user1)
ec.subscribe("warTask", user2)

pub.notify("warTask", 100)
pub.notify("normalTask")

ec.unsubscribe("warTask", user1)
pub.notify("warTask", 200)

单例模式

保证一个类只能被实例一次主题

js
class Single {
  static instance = null
  constructor() {
    this.state = "on"
  }

  static open() {
    this.getInstance()
    this.state = "on"
    return this.instance
  }

  static close() {
    this.getInstance()
    this.state = "off"
    return this.instance
  }

  static getInstance() {
    this.instance ? this.instance : new Single()
  }
}

const foo = Single.open()
const bar = Single.close()
console.log(foo === bar)

装饰器模式

在原本对象上装饰拓展功能

js
class Foo {
  constructor() {}
  say() {
    console.log("Hi")
  }
}

class Bar {
  constructor(foo) {
    this.foo = foo
  }

  say() {
    this.foo.say()
  }

  hear() {
    console.log("Hear")
  }

  read() {
    console.log("read")
  }
}

const foo = new Foo()
const bar = new Bar(foo)
bar.say()
bar.read()

代理模式

代理目标对象,以拦截操作,增加功能

js
const me = {
  name: 'tom',
  age: 18
}

const myLawyer = new Proxy(me, {
  get: (me, property, receiver) => {
    console.log(`receiver is proxy: ${receiver === proxy}`); // 输出 "receiver is proxy: true"
    if (property === 'name') {
      return 'lawyer'
    }
    return me[property]
    // return Reflect.get(target, property, receiver); // 用Reflect最好
  },
  set(me, property, value, receiver) {
    console.log(`you set ${property} to ${value} just now`, me, receiver)

    me[property] = value
    // return Reflect.set(target, property, receiver); // 用Reflect最好
    return true
  }
})

console.log(myLawyer.name) // lawyer

myLawyer.age = 19 // you set age to 19 just now
console.log(myLawyer.age) // 19

Reflect

引伸下

Reflect.get(target, property, receiver) 是 JavaScript 中的一个反射操作,用于获取一个对象的属性值。这个方法接受三个参数:

  • target:目标对象,即要获取属性值的对象。
  • property:要获取的属性的名称。
  • receiver:在获取属性值时作为 this 的值。 如果 property 是一个访问器属性(即它是一个 getter),那么 receiver 将被用作 getter 函数内部的 this 值。如果 property 是一个数据属性,那么 receiver 参数将被忽略。

在 Proxy 对象的 get 陷阱中使用 Reflect.get 可以确保原始对象的行为被正确地复制,包括正确地处理访问器属性。例如:

js
const obj = {
  get foo() {
    return this.bar;
  },
  bar: 1
};

const proxy = new Proxy(obj, {
  get(target, property, receiver) {
    return Reflect.get(target, property, receiver);
  }
});

console.log(proxy.foo); // 输出 1