JavaScript -- 设计模式 行为型设计模式-观察者模式

观察者模式的重要性无容置疑,作为一名前端工程师假如你只学一个设计模式的话,那么毫无疑问应该是观察者模式。

观察者模式:也被称为发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。

早些时候,我们订阅报纸,订阅牛奶等,只要我们交了钱,每天早上小哥骑着自行车给我们送报纸,牛奶。

 

我们以一个简单的例子来了解一下观察者模式。

在react的开发过程中,我们经常会使用redux,那么redux中就使用了观察者模式。(没有使用过也不要紧,我们简单实现一个redux)

function createStore (store) {
    let curState, listnerCallBack = [];

    function getState() {
        return curState;
    }
    function subscribe(cb) {//订阅,将回调函数存入
        listnerCallBack.push(cb);
    }
    function dispatch(action) {//当有action发送时,那么就回触发已经订阅的所有函数
        curState = store(curState, action);
        listnerCallBack.map( v => v() );
    }
    dispatch({ type: "@cyl-redux" });
    return { getState, subscribe, dispatch}
}

const { subscribe, dispatch } = createStore(()=>{});
subscribe(function () {
    console.log("hello");
})
subscribe(function () {
    console.log("CYl");
})

dispatch({});

createStore里面的代码非常简单,我们重点看subscribe和dispatch方法。

subscribe方法是往监听回调函数里面添加方法,当执行dispatch时执行所有注册的回调函数。

 

 

dom事件监听也是一个观察者模式。

const h1 = document.getElementById("title");
h1.addEventListener("click", function (){
    console.log("1");
})
h1.addEventListener("click", function (){
    console.log("2");
})
h1.addEventListener("click", function (){
    console.log("3");
})

当我们点击h1时,那么就会调用这个三个方法

 

 

下个例子就是nodeJS里面的event事件对象

const { EventEmitter } = require("events");

class Demo extends EventEmitter {

}
const d = new Demo();
d.on("data", function () {
    console.log("cyl")
})
d.emit("data");

使用on监听 使用emit发布。

我们现在使用原生JS来模拟一下EventEmitter里面的方法,看一下观察者模式的应用

function EventEmitter () {
    this.event = {};
}
//给类型type增加回调函数 callBack
EventEmitter.prototype.addListener = EventEmitter.prototype.on = function (type, callBack) {
    if (this.event[type]) { //如果之前存在这个类型的事件,就继续添加
        this.event[type].push(callBack);
    } else {
        this.event[type] = [callBack]; //如果不存在,那么就新生成一个数组
    }
}
//调用类型type的所有的回调函数 callBack
EventEmitter.prototype.emit = function (type, ...res) { //参数是从第二个开始
    if (this.event[type]) { //如果存在这个类型的回调函数,那么就执行
        this.event[type].forEach( (listener) => {
            listener.apply(this, res); //调用函数
        })
    }
}
//给类型type增加回调函数 callBack 但是只调用一次
EventEmitter.prototype.once = function (type, listener) {
    function wrap(...res) {//接受参数的
        listener.apply(this, res); //执行 然后立马销毁
        this.removeListener(type, wrap);
    }
    this.on(type, wrap);
}
//移除类型为type,的listener
EventEmitter.prototype.removeListener = function (type, listener) {
    if (this.event[type]) {
        this.event[type] = this.event[type].filter( (item) => item!==listener) //true保留
    }
}

//移除类型为type,所有的listner
EventEmitter.prototype.removeAllListener = function (type) {
    if (this.event[type]) {
        this.event[type] = [];
    }
}

EventEmitter.prototype.listeners = function (type) {
    return this.event[type];
}

还有比如NodeJS的流,也使用观察者模式,使用过nodeJS的应该马上就可以想起来了。

 

最后我们使用观察者模式来实现数据绑定。

熟悉react或者vue的前端工程师都知道,react和vue不需要我们关注视图里面的数据更新,当store里面的数据更新时,那么视图就会自动刷新。

现在我们就使用原生JS利用观察者模式来实现这个功能。

const obj = { name: "cyl" };


class Dep {
    constructor () {
        this.listenCallBack = [];
    }
    add (fn) {
        this.listenCallBack.push(fn);
    }
    notify () {
        this.listenCallBack.map( (v) => { v.update(); } );
    }
}
Dep.target = null;

class Watcher {
    constructor(obj, key, callBack) {
        Dep.target = this;
        this.obj = obj;
        this.key = this.key;
        this.value = obj[key];//触发get 将当前的this也就是watcher保存到listenCallBack
        this.called = callBack;
        Dep.target = null;
    }
    update () {
        this.value = this.obj[this.key];
        this.called();
    }
}
function observe(obj) {
    if (typeof obj === "object" && obj !== null) {
        for (const key in obj) { //对每个属性进行监听
            defineObserve(obj, key, obj[key])
        }
    }
}

function defineObserve(obj, key, value) {
    observe(obj[key]);
    const dep = new Dep();
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: () => {
            if (Dep.target) {
                dep.add(Dep.target); //添加当前的watcher
            }
            return value;
        },
        set: (newValue) => {
            value = newValue;
            dep.notify(); //当被重新设置的时候 那么就调用回调函数
        }
    })
}

observe(obj);

const h1 = document.getElementById("title");

new Watcher(obj, "name", () => {
     
     h1.innerText = obj.name;
})

h1.innerText = obj.name;
setTimeout(() => {
    obj.name = 6;
}, 1000)

觉得自己能力不错,可以去理解上面代码,我写了一些关键注释

我们可以看到一开始页面的显示的是cyl

1秒之后 变成了 6 

 

观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

这是一个很重要的设计模式,在前端JS中非常广泛。

大家可以看左边个人分类的目录,跟着一起学习

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值