观察者模式是前端运用场景最多的,在各大类库以及框架中都能看到它的身影.

一.特点:

  • 发布&&订阅
  • 一对N(一对一,一对多)

二.实现

  1. UML类图
    js设计模式:观察者模式
    Subject类内部保存了一个其订阅者的列表同时还有当前状态:可以通过setState方法改变内部的状态,在状态发生变更的同时执行notifyAllObservers方法,遍历取出每个订阅者,执行update方法.Subject类暴露一个attach方法给Observer类来允许其进行订阅
  2. 代码如下
//要被订阅的主题
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} is updata, subject state is ${this.subject.state}`)
    }
}

const s1 = new Subject()
const o1  = new Observer('o1', s1)
s1.setState(1)

const o2 = new Observer('o2', s1)
const o3 = new Observer('o2', s1)
s1.setState(2)

调用结果如下

o1 is updata, subject state is 1
o1 is updata, subject state is 2
o2 is updata, subject state is 2
o2 is updata, subject state is 2

三.使用场景介绍

  • jquery callback
  • promise
  • node.js自定义事件(stream,http请求处理,多进程通讯)
  • vue中的wacher
  • vue和react中的生命周期钩子函数
1.jquery callback
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p>jQuery callbacks</p>    
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
        var callbacks = $.Callbacks() // 注意大小写
        callbacks.add(function (info) {
            console.log('fn1', info)
        })
        callbacks.add(function (info) {
            console.log('fn2', info)
        })
        callbacks.add(function (info) {
            console.log('fn3', info)
        })
        callbacks.fire('gogogo')
        callbacks.fire('fire')
    </script>
</body>
</html>

结果如下

fn1 gogogo
fn2 gogogo
fn3 gogogo
fn1 fire
fn2 fire
fn3 fire
2. promise
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
//then方法调用
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

可以猜到的是,这里的then方法相当于attach,对消息进行了订阅,而我们的resolvereject方法相当于notifyAllObservers,当结果返回促使promise的state发生之后遍历执行订阅的队列,调用update方法

3.node.js中的自定义事件

node.js中有一个底层的Events模块,被其他的模块大量使用,可以说是node非常核心的一个模块了.其本质还是一个观察者模式

const eventEmitter = require('events').EventEmitter

const emitter1 = new eventEmitter()

emitter1.on('some', info=> {
    console.log('fn1', info)
})
emitter1.on('some', info=> {
    console.log('fn2', info)
})	
emitter1.emit('some', 'xxxx')

输入结果如下

fn1 xxxx
fn2 xxxx

EventEmitter模块能被其他类继承,比如我们可以声明一个Dog类来继承EventEmitter类

class Dog extends EventEmitter {
    constructor(name) {
        super()
        this.name = name
    }
}
var simon = new Dog('simon')
simon.on('bark', function () {
    console.log(this.name, ' barked')
})
setInterval(() => {
    simon.emit('bark')
}, 500)

node.js中实现的stream数据结构继承了EventEmitter类,在读写文件的时候以流的形式进行文件的读写,on(‘data’),on(‘end’)等就是一种事件的订阅

var fs = require('fs')
var readStream = fs.createReadStream('./data/file1.txt')  // 读取文件的 Stream

var length = 0
readStream.on('data', function (chunk) {
    length += chunk.toString().length
})
readStream.on('end', function () {
    console.log(length)
})

在http请求中也是如此

var http = require('http')

function serverCallback(req, res) {
    var method = req.method.toLowerCase() // 获取请求的方法
    if (method === 'get') {
    }
    if (method === 'post') {
        // 接收 post 请求的内容
        var data = ''
        req.on('data', function (chunk) {
            // “一点一点”接收内容
            console.log('chunk', chunk.toString())
            data += chunk.toString()
        })
        req.on('end', function () {
            // 接收完毕,将内容输出
            console.log('end')
            res.writeHead(200, {'Content-type': 'text/html'})
            res.write(data)
            res.end()
        })
    }  
}
http.createServer(serverCallback).listen(8081)
console.log('监听 8081 端口……')

vue中的watch函数

var vm = new Vue({
    el: '#app',
    data: {
        firstName: 'Foo',
        lastName: 'Bar',
        fullName: 'Foo Bar',
    }
    watch: {
        firstName: function(val) {
            this.fullName = val + ' ' + this.lastName
        },
        lastName: function(val) {
            this.fullName = this.firstName + ' ' + val
        }
    }
})

还有vue与react中的生命周期钩子,下面是vue源码,在new Vue的时候使用callHook方法执行我们定义的方法
js设计模式:观察者模式
我们来看看callHook,可以看到的是每个勾子名后面对应的都是一个handlers列表,执行callHook的时候遍历这个列表执行

function callHook (vm, hook) {
    pushTarget();
    var handlers = vm.$options[hook]; //取得handlers列表
    if (handlers) {
      for (var i = 0, j = handlers.length; i < j; i++) {
        try {
          handlers[i].call(vm); //遍历执行
        } catch (e) {
          handleError(e, vm, (hook + " hook"));
        }
      }
    }
    if (vm._hasHookEvent) {
      vm.$emit('hook:' + hook);
    }
    popTarget();
  }

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-09-03
  • 2021-11-29
  • 2021-12-09
  • 2021-06-25
  • 2021-12-22
猜你喜欢
  • 2021-06-28
  • 2021-12-07
  • 2022-01-01
  • 2021-12-24
  • 2021-04-30
  • 2021-06-09
  • 2022-12-23
相关资源
相似解决方案