资讯专栏INFORMATION COLUMN

js中的一对多 - 订阅发布模式

Cobub / 2511人阅读

摘要:解决命名空间问题暂不管,删除订阅问题这个用处不大目前我们先着手解决这个问题对应的消息么有被人订阅没有传入具体的回调函数表示取消对应的所有订阅反向遍历删除订阅回调函数这个对象,能够解决大部分事件模拟的问题。

订阅发布模式如果按数学翻译其实就是.一对多的映射关系.怎么解释呢? 就是一个开关,同时并联几个灯泡(在不同房间),触发的时候,几个灯泡都会得到指令,然后执行发光的行为。

订阅发布模式

这种模式在js里面有这天然的优势,因为js本身就是事件驱动型语言。比如,页面上有一个button, 你点击一下就会触发上面的click事件,而此时有一部分程序正在监听这个事件,随之触发相关的处理程序.

var button = $("#button");
button.on("click",function(){
    console.log("I am pressing the button");
});

事实上,我们也早就熟悉这个模式了,只是不知道这叫什么(订阅发布模式 又名 观察者模式).
这个模式最大的一个好处就在于,能够解耦回调函数,让你的程序看起来更美观(虽然现在有Promise和Deferred帮忙,但是不彻底)。

订阅发布模式内涵

说了点理论,来些干货。(以下将订阅发布模式简称为观察者模式).
观察者模式无非就两个部分,一个订阅(监听程序),一个发布(触发事件).而他们中间的链接枢纽就是事件。 通常来说,我们可以自定义一个观察者模式--使用自定义事件.
(由于IE过于SB,我就不想说他的事了,下面这些适用于chrome, iE9+,ff等其他现代浏览器中)

 // Create the event.  //创建一个事件
var event = document.createEvent("Event"); //Event是自定义的事件名

// Define that the event name is "build".
event.initEvent("build", true, true);  //这里是初始化事件,  //就是些参数而已.

// Listen for the event.
elem.addEventListener("build", function (e) {    //给事件添加监听
  // e.target matches elem
}, false);

// target can be any Element or other EventTarget.
elem.dispatchEvent(event);    //触发事件

大致就是这几个步骤,由于这样写,太非人道了。所以这里映入jquery的trigger触发方式(大哥就是大哥~)
在jquery的事件处理中,几个基本和事件相关的API需要熟悉。一个是on,一个是trigger.

$(".ele").on("click",function(){
    console.log("clicking");
});
$(".ele").trigger("click");

这是一个基本的使用,使用trigger来触发事件.
但是谁尼玛无聊到连click自己手动触发啊,这个例子只是讲解。现在说一下精华-自定义事件.
在jquery里面,可以直接使用on来进行自定义事件的模拟。

ele.on("stimulate",function(){  //订阅一个事件
    ...do sth
});
ele.trigger("stimulate");  //发布一个事件

这里trigger只是起到一个开关的作用,那么我想要他变为一个管道可以吗?
absolutely!!!
在trigger里面还有第二个参数可以选择,即[data]

$(document.body).on("stimulate",function(event,name1,name2){  //和节点有关的事件里,第一个参数永远是event
    console.log(name1,name2);  //"jimmy","sam"
});
$(document.body).trigger("stimulate",["jimmy","sam"]);

而且如果你自定义事件过多,起名也是件死人的事。所以牛逼的jq会帮你把命名空间处理好.

ele.on("stimulate.jimmy.sam",function(){  //使用"."链接
    ...do sth
});

他的作用域就是sam>jimmy>stimulate这样一个关系.
详情可以参考: Aron大神些的jquery事件解析。这里我直接把trigger的源码贴出来吧.以供参考.
trigger源码
其实上面的自定义事件的用法也非常有限,因为如果使用一个节点作为载体的话,这样的成本也太大了。所以一般在业内已经有成熟的自定义事件的插件了.
不过为了深入理解观察者模式,我们一步一步来.(为了装逼)

订阅发布模式进阶

经过上面的唐僧咒,大家也应该差不多熟悉这个模式的一些关键部分。订阅,发布,事件。
好,我们就这3个部分来自己模拟一个。

//摘自alloyTeam团队的曾探·著
var imitate = (function() {
    var imitate = {
        clientList: [],
        listen: function(key, fn) {
            if (!this.clientList[key]) {
                this.clientList[key] = [];
            }
            this.clientList[key].push(fn);
        },
        trigger: function() {
            var key = [].shift.call(arguments);
            var fns = this.clientList[key];

            // 如果没有对应的绑定消息
            if (!fns || fns.length === 0) {
                return false;
            }

            for (var i = 0, fn; fn = fns[i++];) {
                // arguments 是 trigger带上的参数
                fn.apply(this, arguments);
            }
        }
    }
    return function() {
       return Object.create(imitate);
    }
})();
var eventModel = imitate();  //得到上面的对象
eventModel.listen("jimmy",function(){console.log("jimmy");});  //jimmy
eventModel.trigger("jimmy");

恩,这样下来皆可以和重重的节点说拜拜了。直接使用imitate就可以进行事件的模拟,而且超快.
当然,这样写改进的空间还是挺大的。解决命名空间问题(暂不管),删除订阅问题(这个用处不大)...目前我们先着手解决这个问题.

var Event = (function() {
    var clientList = {};
    var listen,
        trigger,
        remove;
    listen = function(key, fn) {
        if (!clientList[key]) {
            clientList[key] = [];
        }
        clientList[key].push(fn);
    };

    trigger = function() {
        var key = [].shift.call(arguments);
        var fns = clientList[key];

        if (!fns || fns.length === 0) {
            return false;
        }

        for (var i = 0, fn; fn = fns[i++];) {
            fn.apply(this, arguments);
        }
    };


    remove = function(key, fn) {
        var fns = clientList[key];

        // key对应的消息么有被人订阅
        if (!fns) {
            return false;
        }

        // 没有传入fn(具体的回调函数), 表示取消key对应的所有订阅
        if (!fn) {
            fns && (fns.length = 0);
        }
        else {
            // 反向遍历
            for (var i = fns.length - 1; i >= 0; i--) {
                var _fn = fns[i];
                if (_fn === fn) {
                    // 删除订阅回调函数
                    fns.splice(i, 1);
                }
            }
        }
    };

    return {
        listen: listen,
        trigger: trigger,
        remove: remove
    }
}());

这个Event对象,能够解决大部分事件模拟的问题。说了这么多,md,实例嘞。。。等等。马上来

发布订阅模式的实战

如果大家写过登录框(异步登录哈),应该知道.登录框和header的部分是完全不同的两个部分。这个场景就很适合发布订阅模式了。
看一下。如果没有发布订阅模式的代码:

login.on("click",function(){
    var name = $(".username").val().trim;
    http.login(name)  //使用异步Deferred书写
    .then(function(data){  //以下填写乱七八糟的处理
        changeName();
        changeAvtar();
        changeStatus();
        ...
    })
});

使用发布订阅模式

login.on("click",function(){
    var name = $(".username").val().trim;
    http.login(name)  //使用异步Deferred书写
    .then(function(data){
        Event.trigger("login",data);  //发布我登录成功的状态,并传入参数
    })
});
var header = (function() {
    Event.listen("login", function(data) {
        header.changeAvator(data);
    })
    return {
        changeAvator: function(data) {
            ...换头像
        }
    }
})();
var bar = (function() {
    Event.listen("login", function(data) {
        bar.changeName(data);
    })
    return {
        changeName: function(data) {
            ...换名字
        }
    }
})();

可以清楚的看到,如果你的登录状态改变了的话,会有一系列的订阅程序发生.而且每个订阅之间互不干扰,你可以随便添加或者删除订阅,这都不会影响你的登录的执行逻辑. 当然发布订阅的使用肯定不会仅仅局限于,登录状态的改变。还可以应用于,模块间信息的传递,分页页面的渲染等。但是使用的时候,一定要慎重,因为你订阅的越多,bug的查找也会越复杂。所以,发布订阅模式使用的时候,希望大家好好想一想,不要为了模式而去模式。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91595.html

相关文章

  • js设计模式 --- 发布订阅模式(观察者模式)

    摘要:由主体和观察者组成,主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。主体并不知道观察者的任何事情,观察者知道主体并能注册事件的回调函数。总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。 发布订阅模式 发布订阅模式又叫观察者模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题...

    EasonTyler 评论0 收藏0
  • 观察者模式的使用介绍

    摘要:观察者模式介绍观察者模式又称发布订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象发生改变的时候,所依赖它的对象都能得到通知。关于内部的观察者模式可以参数这篇文档。总结总之,观察者模式在中的使用是非常广泛的。 javascript观察者模式 介绍 观察者模式又称发布-订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象发生改变的时候,所依赖它的对象都能得到通知。例如:我们订阅...

    ityouknow 评论0 收藏0
  • JS每日一题:设计模式-如何理解观察者(发布订阅)模式?

    摘要:期设计模式如何理解观察者发布订阅模式定义观察者模式又叫发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己生活实例理解你今天去看一个 20190411期 设计模式-如何理解观察者(发布订阅)模式? 定义: 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一...

    baishancloud 评论0 收藏0
  • js 观察者

    摘要:观察者模式机动建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。 观察者模式模式简介 观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。 观察者模式机动...

    missonce 评论0 收藏0
  • Java 设计模式 观察者模式

    摘要:实际上,设计模式就是通过面向对象的特性,将这些角色解耦观察者模式本质上就是一种订阅发布的模型,从逻辑上来说就是一对多的依赖关系。在添加一个观察者时,把被主题被观察者对象以构造函数的形式给传入了观察者。 每个角色都对应这一个类,比如观察者模式,观察者对应着观察者类,被观察者对应着被观察者类。实际上,设计模式就是通过面向对象的特性,将这些角色解耦 观察者模式本质上就是一种订阅 / 发布的模...

    马忠志 评论0 收藏0

发表评论

0条评论

Cobub

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<