您的位置:首页 > Web前端 > JavaScript

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

2017-10-11 11:06 453 查看
熟悉一个老项目的过程中,许多业务功能是在前端完成的,其中有一个技术点就是如何完成主页面与子页面之间的通信,其中很多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('广播事件必须要指定事件类型');

aa52
}
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对象
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: