背景:

  在现在WEB开发中,稍复杂一点的页面,都会涉及到多个模块,尤其是类似seajs、LABjs、requireJS等模块工具出来后,前端开发者分模块开发已经慢慢变成一种习惯了,但是多个模块间的常常有各种交集,需要通信、需要互相调用,这时监听者模式便是必不可少的一种设计模式了,在前端表现事件操作。backbone和jquery都有提供了很好的事件处理方式。

  但是,真正开发需求的时候我们常常会遇到文件加载顺序跟事件监听与更新不一致的需求,比如说:在一个视频网站里面,有一个视频处理的JS模块和用户信息处理模块,现在需要先加载视频模块的JS使得能第一时间把视频展示给用户看,但是在展示完视频的同时需要视频JS模块会更新一个在用户信息处理模块里面监听的发送用户查看视频记录的模块,这时候由于用户信息处理模块未加载,所以会导致事件未定义且不被执行,这时我们希望的是就算是之前更新的,用户信息处理模块在初始化监听时能知道之前已经被更新过了,并执行该更新。下面就是来实现不依赖JS加载顺序事件对象的实现吧:

基础:

(function(){
var obListenList = {}; //监听者事件列表
var OB = {
listen : function(listenStr,func){
if(!obListenList[listenStr]){
obListenList[listenStr] = [];
}
obListenList[listenStr].push(func);
},
update : function(listenStr,datas){
if(typeof obListenList[listenStr] != "undefined"){
var len = obListenList[listenStr].length,
i = 0;
for( ; i < len ; i++){
if(typeof(obListenList[listenStr][i])=="function"){
obListenList[listenStr][i](datas);
}
}
}
}
};
window.OB = OB;
}()); OB.listen("say",function(msg){
document.write(msg + "<br/>");
});
OB.update("say","She say : Im your's!");

  基础的事件编写很简单,只需要用一个对象把被监听的事件的函数列表放在一个对象(obListenList)里面,在update的时候查看遍历调用就是了。

实现先更新,等监听的时候也会执行之前的更新:

(function(){
var obListenList = {}, //监听事件列表
obUpdateList = {} , // 更新事件列表
obUpdateListLen = 2 , //最大监听更新队列数
obUpdateWaitFlag = true ; var OB = {
listen : function(listenStr,func){
if(!obListenList[listenStr]){
obListenList[listenStr] = [];
}
obListenList[listenStr].push(func); //查看之前是否有更新过
if(typeof obUpdateList[listenStr] != "undefined"){
var lStrArg = obUpdateList[listenStr];
for(var i = 0 ; i < lStrArg.length; i ++ ){
func(lStrArg[i]);
}
} },
update : function(listenStr,datas){ if(typeof obListenList[listenStr] != "undefined"){
var len = obListenList[listenStr].length,
i = 0;
for( ; i < len ; i++){
if(typeof(obListenList[listenStr][i])=="function"){
obListenList[listenStr][i](datas);
}
}
}
if(obUpdateWaitFlag){ //往该事件更新队列里面添加数据
if(typeof obUpdateList[listenStr] == "undefined"){
obUpdateList[listenStr] = [];
}
obUpdateList[listenStr].push(datas); //如果超过允许的最大长度就不要再往里面添加了,防止内在溢出
if(obUpdateList[listenStr].length>=obUpdateListLen){
obUpdateWaitFlag = false;
}
}
}
};
window.OB = OB;
}()); OB.update("say","She say : Im your's!--1");
OB.update("say","She say : Im your's!--2");
OB.update("say","She say : Im your's!--3");
OB.listen("say",function(msg){
document.write(msg + "<br/>");
});
OB.update("say","She say : Im your's!--4"); /* 结果是:
* She say : Im your's!--1
* She say : Im your's!--2
* She say : Im your's!--4
*/

  看下代码,实现起来也不是很难有三个关键点:

    1,在更新事件的时候将事件传入的参数传进更新事件列表里面

    2,在监听事件初始化后,查看更新事件列表里面有没有对应事件需要更新的数据,如果有,便遍历执行一遍

    3,一定要给更新事件列表设定一个最大保留监听的事件长度(obUpdateListLen),因为在一些复杂的页面程序中可能会有某个无限的更新,如果不做限制必然导致内存溢出。

优化:

(function() {

    if(typeof OB !== "undefined"){//防止文件重复加载导致事件队列被清空
return ;
} var obListenList = {} , //存储监听队列
obUpdateList = {} , //存储更新队列
obUpdateListLen = 2 , //存储更新队列最多保存长度
obUpdateWaitFlag = true ; //记录是否达到了存储更新队列的最大值 var OB = function (listenStr){
return new OB.prototype.init(listenStr);
}; OB.prototype = {
listenStr : "",
instance : null,
init : function(listenStr){
if(OB.prototype.instance){
OB.prototype.instance.setListenStr(listenStr);
return OB.prototype.instance;
} this.setListenStr(listenStr); OB.prototype.instance = this;
return this;
},
setListenStr : function(listenStr){
this.listenStr = listenStr;
},
listen : function(func){
if(this.listenStr){
if(!obListenList[this.listenStr]){
obListenList[this.listenStr] = [];
}
obListenList[this.listenStr].push(func); //查看之前是否有更新过
if(typeof obUpdateList[this.listenStr] != "undefined"){
var lStrArg = obUpdateList[this.listenStr];
for(var i = 0 ; i < lStrArg.length; i ++ ){
func.apply(this,lStrArg[i]);
}
}
}
},
update : function(){
if(typeof obListenList[this.listenStr] != "undefined"){
var len = obListenList[this.listenStr].length,
i = 0;
for( ; i < len ; i++){
if(typeof(obListenList[this.listenStr][i])=="function"){
obListenList[this.listenStr][i].apply(this,arguments);
}
}
}
if(obUpdateWaitFlag){
if(typeof obUpdateList[this.listenStr] == "undefined"){
obUpdateList[this.listenStr] = [];
}
obUpdateList[this.listenStr].push(arguments); if(obUpdateList[this.listenStr].length>=obUpdateListLen){
obUpdateWaitFlag = false;
}
}
}
}; OB.prototype.init.prototype = OB.prototype;
window.OB = OB; //兼容seaJS和requireJS
if ( typeof define === "function" && ( define.cmd || define.amd ) ) {
define( "observer", [], function() {
return OB;
});
} }()); OB("say").update("She say : Im your's!--1");
OB("say").update("She say : Im your's!--2");
OB("say").update("She say : Im your's!--3");
OB("say").listen(function(msg){
document.write(msg + "<br/>");
});
OB("say").update("He Say : yes! you are mine!"); /* 结果是:
* She say : Im your's!--1
* She say : Im your's!--2
* He Say : yes! you are mine!
*/

  这里主要是几个点的优化:

  1,操作优化,使用了JQuery原型模式实现了JQuery操作方式,具体的原理可以参考《犀利开发:jQuery内核详解与实践》 这本书

  2,支持AMD和CMD模块,方便直接应用到SeaJS或requireJS等项目

  3,防止重复加载导致事件列表被清空

这个只是本人的实践经历,写法可能没有那么优美,但是基本功能已经出来了,希望对大家的项目有所帮助,如果有存在什么问题,也可以回复一下。

javascript不依赖JS加载顺序事件对象实现的更多相关文章

  1. (转)JS加载顺序

    原文:http://blog.csdn.net/dannywj1371/article/details/7048076 JS加载顺序 做一名合格的前端开发工程师(12篇)——第一篇 Javascrip ...

  2. 组件推荐Forloop.HtmlHelpers 用来实现MVC的js加载顺序

    最近在开发的时候遇到js加载顺序的问题,layui在底部声明了js,但是我想在页面其他地方使用分布视图,分布视图内有自己的js逻辑,发现不能执行,一看就发现,这里的js应该加在layui后面执行才能有 ...

  3. cookie.js 加载顺序问题

    今天遇到一个问题,在使用cookie.js时,只有在jquery.js文件后加载整体才有效 有效加载顺序 <head> <script type="text/javascr ...

  4. 记录一个bootstrap因js加载顺序导致的问题(tstrap-table-mobile.min.js:7 Uncaught TypeError: Cannot read property 'defaults' of undefined)

    问题描述: 网上找了会没看到答案,然后看了下源码,发现也没有问题,想到js加载的顺序,改了下,发现问题没了. 正确的顺序: 我之前把 <script src="/js/plugins/ ...

  5. 小程序页面的四种文件(JSON、WXML、WXSS、JS)加载顺序

    一个小程序页面由四种文件组成: 1)json 页面配置文件 2)js 页面逻辑文件(必需) 3)wxml 页面结构文件(必需) 4)wxss 页面样式文件 这四个文件的加载顺序: 第一步: 加载页面j ...

  6. html,css,js加载顺序

    1.js放在head中会立即执行,阻塞后续的资源下载与执行.因为js有可能会修改dom,如果不阻塞后续的资源下载,dom的操作顺序不可控. 正常的网页加载流程是这样的. 浏览器一边下载HTML网页,一 ...

  7. js加载顺序

    在jsp页面中引js文件,页面按照js顺序加载,若js中存在相同的方法,应该会执行顺序在前的js中的方法.有时候遇到js方法不执行或者执行不对的情况,可能就是js的顺序问题,有时候会找很久找不到问题. ...

  8. 【Spring】依赖注入 加载顺序

    一.Spring依赖注入depents-on参数 depents-on是指指定Bean初始化及销毁时的顺序,使用depends-on属性指定的是Bean要先初始化完毕后才初始化当前Bean,由于只有S ...

  9. JS 页面加载触发事件 document.ready和window.onload的区别

    document.ready和onload的区别——JavaScript文档加载完成事件页面加载完成有两种事件: 一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件): 二是onlo ...

随机推荐

  1. jQuery Mobile + HTML5 获取地理位置信息

      这个代码也非常简单,核心是HTML5中GeoLocation API,函数原型定义如下: void getCurrentPosition(in PositionCallback successCa ...

  2. IIS 7浏览网站出错,错误编码 http 503 service unable

    由于重新装了Microsoft Visual Studio 2010,结果运行出错,检查发现应用程序池该项目已停止. 解决办法: 1.打开IIS:开始菜单>运行 2.输入“inetmgr”回车 ...

  3. redis之(十二)redis数据的持久化

    [一]redis的数据为什么要持久化 --->redis的存取数据性能高,是由于将所有数据都存储在内存中.当redis重启的时候,存储在内存中的数据就容易丢失. --->把redis作为数 ...

  4. 181. Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  5. NodeJs中使用jQuery?

    在NodeJs中使用jQuery? 有时候在项目中需要使用jq在node中,但是使用起来却不是那么友好,那么现在该怎么做?改写JQ插件?将JQ插件打包成npm包,再在项目中进行引用?显然这些相比较于难 ...

  6. CentOS 6.5,SSH安装与配置

    #rpm -qa |grep ssh 检查是否装了SSH包 #yum install openssh-server 没有的话,安装SSH服务 #chkconfig --list sshd 检查SSHD ...

  7. oracle 11g安装教程

    oracle 11g安装教程 第1步 第2步 第3步 第4步 第5步 第6步 第7步 第8步 第9步 第10步 第11步 第12步 第13步 第14步 第15步 第16步 第17步 第18步 第19步 ...

  8. Linux搭建主从数据库服务器(主从复制)

    配置主机数据库: 1.克隆linux操作系统 2.修改Linux系统主机IP地址 主机IP:192.168.247.150 从机IP:192.168.247.151 3.通过xshell连接Maste ...

  9. 【转】Django中的cookie与session

    转自:https://www.cnblogs.com/chenchao1990/p/5283725.html cookie与session的实现原理 HTTP被设计为”无状态”,每次请求都处于相同的空 ...

  10. [hdu-3007]Buried memory 最小覆盖圆

    大致题意: 平面上有n个点,求一个最小的圆覆盖住所有点 最小覆盖圆裸题 学习了一波最小覆盖圆算法 #include<cstdio> #include<iostream> #in ...