# js设计模式

我觉得要想深入学习 javascript,需要学习框架的源码。

了解源码之前需要知道 javascript 设计模式。

# 1、发布订阅模式

发布订阅模式叫观察者模式。

举一个生活中的例子,很多了订阅了一个博客,相当于触发了一个 on 事件(下面会介绍),当博客的发件人发布一篇博客时,会通知每一个订阅博客的人,相当于触发了 emit事件。

实际应用有 Vue 中的 $on$emit 方法。

(1)介绍

这样说可能难理解,简单的说,写一个构造函数,像这样:

function EventEmitter(){
}

现在我们要订阅一件事,就是给构造函数添加一个方法,像这样:

EventEmitter.prototype.on = function(name, cb){
}
// 参数1:函数名
// 参数2:要执行的函数

事件订阅好了,该发布了,这样:

EventEmitter.prototype.emit = function(name,...args){
}
// 参数1: 要触发的函数名
// 参数2: 需要给函数传入的参数

当时机到时,你通过 emit 传入一个函数名来执行自己定义好的函数。

如果我们以后不用一个事件,那么应该把他移除掉对吧。

EventEmitter.prototype.off = function(name){
}

骨架搭建好了,先模拟用一下,不着急实现,首先要知道怎么用对吧。

// 创建实例
var emitter = new EventEmitter();

// 订阅一个事件
emitter.on("sayHi", function(data){
    console.log(data)
})

// 发布一个消息
emitter.emit("sayHi","Hi") // 执行sayHi函数,应该打印 Hi

这个就是大概的流程,下面我们开始实现。

(2)实现

我们看到,在 on 里有一个回调函数,在emit 的时候执行,所以我们在构造函数中添加一个对象,保存这些函数。

function EventEmitter(){
    this.list = {}
}

现在可以实现 on 方法了,第一个参数函数名,第二个参数为函数。

EventEmitter.prototype.on = function (name, cb) {
    if (this.list[name]) {
        this.list[name].push(cb) // 追加方法
    } else {
        this.list[name] = [cb]; // 创建一个name事件
    }
}

我们先应该判断一下是否有这个方法,要不然会覆盖之前写的方法,如果存在我们追加这个函数即可。

不存在这个方法,我们以 name 为名字创建一个数组,把函数添加即可。

现在实现 emit 方法,当触发 emit 我们应该执行对应的函数。

EventEmitter.prototype.emit = function (name, ...args) {
    let _this = this;
    // 如果事件不存在返回 false
    if (!this.list[name]) return false;

    // 遍历 this.list[name] 中的每一个函数并执行
    this.list[name].forEach(fn => {
        fn.apply(this, args);
    })
    return true;
}

还有一个 off 方法,取消订阅。

EventEmitter.prototype.off = function (name, fn) {
    // 如果事件不存在返回 false
    if (!this.list[name]) return false;

    // 循环数组找到该函数并移除
    for (let i = 0; i < this.list[name].length; i++) {
        if (this.list[name][i] == fn) {
            this.list[name].splice(i, 1);
        }
    }
    return true
}

基本功能实现了,使用试一下:

var emitter = new EventEmitter();

// 用户1 添加订阅
function sayHi1(data) {
    console.log(data, "user1")
}
emitter.on("sayHi", sayHi1)

// 用户2 添加订阅
function sayHi2(data) {
    console.log(data, "user2")
}
emitter.on("sayHi", sayHi2)

// 取消 user1 的订阅
emitter.off("sayHi", sayHi1)

// 发布
emitter.emit("sayHi", "订阅者")

// 打印
// 订阅者 user2

用 class 语法重写了一下完整的。

class EventEmitter {
    constructor() {
        this.list = {}
    }
    on(name, cb) {
        if (this.list[name]) {
            this.list[name].push(cb) // 追加方法
        } else {
            this.list[name] = [cb]; // 创建一个name事件
        }
    }
    emit(name, ...args) {
        let _this = this;
        // 如果事件不存在返回 false
        if (!this.list[name]) return false;

        // 遍历 this.list[name] 中的每一个函数并执行
        this.list[name].forEach(fn => {
            fn.apply(_this, args);
        })
        return true;
    }
    off(name, fn) {
        // 如果事件不存在返回 false
        if (!this.list[name]) return false;

        // 循环数组找到该函数并移除
        for (let i = 0; i < this.list[name].length; i++) {
            if (this.list[name][i] == fn) {
                this.list[name].splice(i, 1);
            }
        }
        return true
    }
}
更新时间: 12/26/2021, 1:44:08 PM