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

释放双眼,带上耳机,听听看~!

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

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

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

 

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1function createStore (store) {
2    let curState, listnerCallBack = [];
3
4    function getState() {
5        return curState;
6    }
7    function subscribe(cb) {//订阅,将回调函数存入
8        listnerCallBack.push(cb);
9    }
10    function dispatch(action) {//当有action发送时,那么就回触发已经订阅的所有函数
11        curState = store(curState, action);
12        listnerCallBack.map( v => v() );
13    }
14    dispatch({ type: "@cyl-redux" });
15    return { getState, subscribe, dispatch}
16}
17
18const { subscribe, dispatch } = createStore(()=>{});
19subscribe(function () {
20    console.log("hello");
21})
22subscribe(function () {
23    console.log("CYl");
24})
25
26dispatch({});
27

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

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

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

 

 

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


1
2
3
4
5
6
7
8
9
10
11
1const h1 = document.getElementById("title");
2h1.addEventListener("click", function (){
3    console.log("1");
4})
5h1.addEventListener("click", function (){
6    console.log("2");
7})
8h1.addEventListener("click", function (){
9    console.log("3");
10})
11

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

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

 

 

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


1
2
3
4
5
6
7
8
9
10
11
1const { EventEmitter } = require("events");
2
3class Demo extends EventEmitter {
4
5}
6const d = new Demo();
7d.on("data", function () {
8    console.log("cyl")
9})
10d.emit("data");
11

使用on监听 使用emit发布。

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1function EventEmitter () {
2    this.event = {};
3}
4//给类型type增加回调函数 callBack
5EventEmitter.prototype.addListener = EventEmitter.prototype.on = function (type, callBack) {
6    if (this.event[type]) { //如果之前存在这个类型的事件,就继续添加
7        this.event[type].push(callBack);
8    } else {
9        this.event[type] = [callBack]; //如果不存在,那么就新生成一个数组
10    }
11}
12//调用类型type的所有的回调函数 callBack
13EventEmitter.prototype.emit = function (type, ...res) { //参数是从第二个开始
14    if (this.event[type]) { //如果存在这个类型的回调函数,那么就执行
15        this.event[type].forEach( (listener) => {
16            listener.apply(this, res); //调用函数
17        })
18    }
19}
20//给类型type增加回调函数 callBack 但是只调用一次
21EventEmitter.prototype.once = function (type, listener) {
22    function wrap(...res) {//接受参数的
23        listener.apply(this, res); //执行 然后立马销毁
24        this.removeListener(type, wrap);
25    }
26    this.on(type, wrap);
27}
28//移除类型为type,的listener
29EventEmitter.prototype.removeListener = function (type, listener) {
30    if (this.event[type]) {
31        this.event[type] = this.event[type].filter( (item) => item!==listener) //true保留
32    }
33}
34
35//移除类型为type,所有的listner
36EventEmitter.prototype.removeAllListener = function (type) {
37    if (this.event[type]) {
38        this.event[type] = [];
39    }
40}
41
42EventEmitter.prototype.listeners = function (type) {
43    return this.event[type];
44}
45

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

 

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

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

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
1const obj = { name: "cyl" };
2
3
4class Dep {
5    constructor () {
6        this.listenCallBack = [];
7    }
8    add (fn) {
9        this.listenCallBack.push(fn);
10    }
11    notify () {
12        this.listenCallBack.map( (v) => { v.update(); } );
13    }
14}
15Dep.target = null;
16
17class Watcher {
18    constructor(obj, key, callBack) {
19        Dep.target = this;
20        this.obj = obj;
21        this.key = this.key;
22        this.value = obj[key];//触发get 将当前的this也就是watcher保存到listenCallBack
23        this.called = callBack;
24        Dep.target = null;
25    }
26    update () {
27        this.value = this.obj[this.key];
28        this.called();
29    }
30}
31function observe(obj) {
32    if (typeof obj === "object" && obj !== null) {
33        for (const key in obj) { //对每个属性进行监听
34            defineObserve(obj, key, obj[key])
35        }
36    }
37}
38
39function defineObserve(obj, key, value) {
40    observe(obj[key]);
41    const dep = new Dep();
42    Object.defineProperty(obj, key, {
43        enumerable: true,
44        configurable: true,
45        get: () => {
46            if (Dep.target) {
47                dep.add(Dep.target); //添加当前的watcher
48            }
49            return value;
50        },
51        set: (newValue) => {
52            value = newValue;
53            dep.notify(); //当被重新设置的时候 那么就调用回调函数
54        }
55    })
56}
57
58observe(obj);
59
60const h1 = document.getElementById("title");
61
62new Watcher(obj, "name", () => {
63    
64     h1.innerText = obj.name;
65})
66
67h1.innerText = obj.name;
68setTimeout(() => {
69    obj.name = 6;
70}, 1000)
71

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

我们可以看到一开始页面的显示的是cylJavaScript -- 设计模式 行为型设计模式-观察者模式

1秒之后 变成了 6 JavaScript -- 设计模式 行为型设计模式-观察者模式

 

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

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

 

 

给TA打赏
共{{data.count}}人
人已打赏
安全技术

c++ list, vector, map, set 区别与用法比较

2022-1-11 12:36:11

安全漏洞

Vaadin 6.4.9 发布,修复XSS漏洞

2011-1-12 11:12:22

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索