类似观察者设计模式的JS,问题总结

熟悉一个老项目的过程中,许多业务功能是在前端完成的,其中有一个技术点就是如何完成主页面与子页面之间的通信,其中很多js看起来很模糊,后来问同事了解到我们这个项目用到了这么一个js文件

主要功能如下
/*
 *  一个类似观察者设计模式 使用的WEB前端发布(publish) 订阅(subscribe) 的小组件 不需要第3方库的依赖
 *  * 支持像jquery一样的链式调用风格
 *  *使用ps.pub / ps.publish    发布事件通知
 *  *使用ps.sub /ps.subscribe   订阅通知
 */
!function(w){
    var ps={},
    subs={},
    tokencache=1,
    tokenPre="pubAndSub-";
    /**
     * 广播订阅者
     */
    ps.publish=ps.pub=function(){
        var args=getArgsAsArrays(arguments);
        if(args.length<=0){
            throw new Error('广播事件必须要指定事件类型');
        }
        var event=args[0];
        var calls=subs[event];
        if(!calls){//不处理没有用户订阅的事件
            return;
        }
        var ctx = { event: event, args:args.splice(1)},
        len=calls.length;
        for(var i=0;i<len;i++){
            ctx.token=calls[i].token;
            calls[i].callback.apply(ctx,ctx.args);
        }
        return this;
    }
    /**
     * 订阅
     */
    ps.subscribe=ps.sub=function(event,call){
        var a=subs[event]=subs[event]||[],
        instanceToken=tokenPre+tokencache++;
        a.push({
            token:instanceToken,
            callback:call
        });
        return instanceToken;
    }


    //util
    /**
     * 获取参数列表 转换为[]
     */
    function getArgsAsArrays(arg){
        return Array.prototype.slice.call(arg);
    }

    w.ps=ps;
}(window);
需要先subscribe,后publish,我们做一个小演示
var person = {
                name: "ning",
                sayHi: function() {
                    console.log("person sub......");
                }
            };
var msg = ps.sub(person,person.sayHi);
ps.pub(person);
我们看看调用sub的代码
ps.subscribe=ps.sub=function(event,call){
        var a=subs[event]=subs[event]||[],
        instanceToken=tokenPre+tokencache++;
        a.push({
            token:instanceToken,
            callback:call
        });
        return instanceToken;
    }
在整个JS中先定义了subs这个对象,当我们调用ps.sub的时候,为subs添加了一个event属性,值为[],一个空数组,并将这个空数组的引用给了a,instanceToken只是调用subs时返回的信息,暂且忽略,接着我们,为这个空数组传入了对象,subs这个对象就变成了:
{
    Object: [{callback:ƒ (),token:"pubAndSub-1" }]
}
其中这个Object就是person对象,ƒ ()就是sayHi那个函数,
那么在调用pub的时候做了什么呢
ps.publish=ps.pub=function(){
        var args=getArgsAsArrays(arguments);
        if(args.length<=0){
            throw new Error('广播事件必须要指定事件类型');
        }
        var event=args[0];
        var calls=subs[event];
        if(!calls){//不处理没有用户订阅的事件
            return;
        }
        var ctx = { event: event, args:args.splice(1)},
        len=calls.length;
        for(var i=0;i<len;i++){
            ctx.token=calls[i].token;
            calls[i].callback.apply(ctx,ctx.args);
        }
        return this;
    }
getArgsAsArrays是将arguments对象转成数组,在ps.pub(person)时,args就是一个数组,只包含person对象这一个值。
var event=args[0];    //这一行取出数组第一个元素,即person对象
var calls=subs[event];    //拿到这个对象的所有回调函数组成的数组
var ctx = { event: event, args:args.splice(1)}    //其中这个对象中的event就是我们的person对象,args.splice(1)则删除了第一个元素,只留下了需要传的参数,在我们这次调用中,只留下一个空数组,因为不需传参。

在for循环中对所有回调函数,以ctx这个对象为作用域,传入args参数,执行函数。
于是在控制台中看到

person sub......

但是如果我们将person中的sayHi改为如下代码时
var person = {
                name: "ning",
                sayName: function() {
                    console.log(this.name);
                }
            };
var msg = ps.sub(person,person.sayName);
ps.pub(person);     //undefined
person.sayName();   //ning

这就牵扯到作用域的问题,关键字为apply(),this
当person.sayName()的时候,this代表的是person这个作用域,所欲this.name即ning
但是当使用观察者模式时,调用sayName的代码变为了如下代码

calls[i].callback.apply(ctx,ctx.args);

apply函数的作用是改变函数的作用域,此时调用sayName()的作用域为ctx,ctx中没有name属性,自然就是undefined,如果想要输出名字,则需要将sayName中的代码改为

console.log(this.event.name);//ning
其中的this.event就是ctx对象中的event属性,本次调用中即为person对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值