首先很感谢bingo,用非常新颖的思路,实现了这么一个js的loader工具。往外抛异常阻止函数运行,并在加载完后继续运行caller的做法,让人眼前一亮。不得不佩服js的世界是如此的奇妙。
在阅读bingo的js loader,获得启发的同时,也对其中的代码功能做了一些调整。例如,添加了绑定事件机制,可以预先绑定需要在加载(loaded)完后触发的操作(事件一共有5类)。规范了消息队列的实现,bingo的实现中,把消息队列耦合的太紧密。所以独立实现了一个消息队列,在loader中使用等等。整个代码经过重写,并加了注释,方便爱好者学习和探讨,也希望大家反馈bug。并且允许在include时,设置caller的scope和arguments,这样就可以在一般的函数中,比如实例方法(而不只是在匿名函数)使用include。因为,bingo的实现中,在调用caller时,只是简单的运行caller,如果在有需要作用域和需要参数传递的函数中,会失去原本的作用域和传递进来的参数。
最后,还是非常感谢bingo!
代码如下(代码下载在最后):
js 代码
- var jsLoader=new function() {
- /*
- * const
- */
- this.NAMESPACE_TYPE={
- CLASS:'class',
- INTERFACE:'interface',
- OBJECT:'object'
- };
- this.LOADMODE_TYPE={
- PACKAGE:'package',
- FILE:'file',
- DYNAMIC:'dynamic'
- };
- this.ERROR_LEVEL={
- INFO:'@jsadk:',
- DEBUG:'*@jsadk:',
- FATAL:'!*@jsadk:',
- THROWS:'^!*@jsadk:'
- };
- this.ERRORS={};
- /*
- * private member
- */
- var _this=this;
- var _version='1.0a';
- var _libPath='./';
- var _errLv=this.ERROR_LEVEL.FATAL;
- var _loadmode=this.LOADMODE_TYPE.PACKAGE;
- var _packages={};
- /*
- * getter and setter
- */
- this.getVersion=function() {
- return _version;
- };
- this.getLibPath=function() {
- return _libPath;
- };
- this.setLibPath=function(libPath) {
- _libPath=libPath;
- };
- this.getLoadMode=function() {
- return _loadmode;
- };
- this.setLoadMode=function(loadmode) {
- _loadmode=loadmode;
- };
- this.getErrLevel=function() {
- return _errLv;
- }
- this.setErrLevel=function(level) {
- _errLv=level;
- };
- /*
- * register packages infos
- */
- /**
- * 增加包信息
- * @param {Object} root
- * @param {Hash} pks
- */
- var _addPKGs=function(root,pks) {
- for (var pk in pks) {
- if (typeof pks[pk]=='string') {
- root[pk]=_this.Util.initVar(root[pk],pks[pk]);
- } else if(typeof pks[pk]=='object') {
- root[pk]=_this.Util.initVar(root[pk],{});
- _addPKGs(root[pk],pks[pk]);
- }
- }
- };
- /**
- * 获得注册的包信息
- * @param {String|Object} pack
- */
- this.getPKGs=function(pack) {
- return _this.Util.eval(pack,_packages);
- };
- /**
- * 注册包信息
- * @param {Hash} pkgs
- * @param {String} ns
- */
- this.regPKGs=function(pkgs,ns) {
- var size=arguments.length;
- var ns=ns.split('.');
- var root=_packages;
- for (var i=0;i
- root=root[ns[i]]=_this.Util.initVar(root[ns[i]],{});
- }
- _addPKGs(root,pkgs);
- };
- /*
- * catch the window error
- */
- window.onerror=function() {
- if (arguments[0].indexOf(_errLv)>-1) {
- return false;
- } else {
- return true;
- }
- };
- };
- with(jsLoader) {
- /*
- * utility methods
- */
- jsLoader.Util={
- /**
- * 一个空函数的引用
- */
- emptyFunction:function() {},
- /**
- * 在给定的scope中执行代码
- * @param {String} obj
- * @param {Object} scope[default=window]
- */
- eval:function(obj,scope) {
- scope=(scope==null)?window:scope;
- if (typeof obj=='string') {
- try {
- with(scope) {
- obj=eval(obj);
- }
- } catch (e) {
- return undefined;
- }
- }
- return obj;
- },
- /**
- * 检查在给定的scope中对象是否存在
- * @param {String|Object} obj
- * @param {Object} scope[default=window]
- */
- exist:function(obj,scope) {
- return (typeof this.eval(obj,scope)=='undefined'?false:true);
- },
- /**
- * 用给定的值initVal,初始化变量obj,并返回obj。如果obj不为undefined,则直接返回obj
- * @param {Object} obj
- * @param {Object} initVal
- */
- initVar:function(obj,initVal) {
- if (typeof obj=='undefined') {
- obj=initVal;
- }
- return obj;
- },
- /**
- * 用source扩展destination,并返回destination。
- * fProperty和fValue用来筛选source中的属性和值
- * @param {Object} destination
- * @param {Object} source
- * @param {Function} fProperty(property,destination)[scope=source]
- * @param {Function} fValue(value,property,destination)[scope=source]
- */
- extend:function(destination,source,fProperty,fValue) {
- for (var property in source) {
- var _property=(fProperty || Util.emptyFunction).call(source,property,destination);
- if (_property!=false) {
- var _value=(fValue!=null?
- fValue.call(source,source[property],property,destination):
- source[property]);
- destination[typeof _property=='string'?_property:property]=_value;
- }
- }
- return destination;
- }
- };
- /*
- * extends Array
- */
- (function() {
- Util.extend(Array.prototype,{
- first:function() {
- return this[0];
- },
- last:function() {
- return this[this.length-1];
- },
- /**
- * 给出element在队列中所处的位置索引(第一个),如果不存在则返回-1。参数struct为true是进行严格比较
- * @param {Object} element
- * @param {Boolean} struct[default=false]
- */
- indexOf:function(element,struct) {
- var size=this.length;
- for (var i=0;i
- if ((struct && this[i]===element) ||
- (!struct && this[i]==element)) {
- break;
- }
- }
- return (i==size?-1:i);
- },
- /**
- * 在索引index处插入元素element1,element2,....
- * @param {Number} index
- */
- insertAt:function(index) {
- var args=[index,0];
- var size=arguments.length;
- for (var i=1;i
- args.push(arguments[i]);
- }
- this.splice.apply(this,args);
- },
- /**
- * 删除索引index处的size个元素,并返回删除的元素数组
- * @param {Number} index
- * @param {Number} size[opt=1]
- */
- deleteAt:function(index,size) {
- result=this.splice(index,size || 1);
- return result;
- },
- /**
- * 清空队列
- */
- clear:function() {
- this.length=0;
- }
- });
- })();
- /**
- * 事件队列,用于注册和注销事件。并触发对应事件
- */
- jsLoader.EventQueue=(function() {
- Util.extend(ERRORS,{
- 'EventQueue.NO_SUCH_EVENT':ERROR_LEVEL.DEBUG+'no such a event',
- 'EventQueue.ALEADY_EXIST':ERROR_LEVEL.DEBUG+'the message is already exsited!'
- });
- var constructor=function(event) {
- this._queue={};
- this.EVENT={};
- for (var e in event) {
- this.EVENT[event[e]]=true;
- this._queue[event[e]]=[];
- }
- };
- Util.extend(constructor.prototype,{
- /**
- * 触发队列中,对应事件event的消息。参数dispose为ture时,将在触发完后删除该事件的所有消息。
- * @param {String} event
- * @param {Boolean} dispose[default=false]
- * @param {Array} args[default=[]]
- */
- dispatch:function(event,dispose,args) {
- if (this.EVENT[event]) {
- var messages=this._queue[event];
- var size=messages.length;
- try {
- for (var i=0;i
- messages[i++].apply(messages[i++],messages[i++].concat(args || []));
- }
- } catch(e) {
- throw new Error(ERROR_LEVEL.DEBUG+e.message);
- } finally {
- if (dispose) {
- this._queue[event].clear();
- }
- }
- } else {
- throw new Error(ERRORS['EventQueue.NO_SUCH_EVENT']+':'+event);
- }
- },
- /**
- * 注册事件event的消息message。scope和args参数指定了消息调用时的作用域和参数。
- * @param {String} event
- * @param {Function} message
- * @param {Object} scope[default=window]
- * @param {Array} args[default=[]]
- */
- on:function(event,message,scope,args) {
- if (this.EVENT[event]) {
- if (this._queue[event].indexOf(message)==-1) {
- this._queue[event].push(message);
- this._queue[event].push(scope || window);
- this._queue[event].push(args || []);
- } else {
- throw new Error(ERRORS['EventQueue.ALEADY_EXIST']+':'+event);
- }
- } else {
- throw new Error(ERRORS['EventQueue.NO_SUCH_EVENT']+':'+event);
- }
- },
- /**
- * 注销事件event的小心message
- * @param {String} event
- * @param {Function} message
- */
- un:function(event,message) {
- var index;
- if (this.EVENT[event] && (index=this._queue[event].indexOf(message))!=-1) {
- this._queue[event].deleteAt(index,3);
- } else {
- throw new Error(ERRORS['EventQueue.NO_SUCH_EVENT']+':'+event);
- }
- }
- });
- return constructor;
- })();
- /*
- * a class of message queue, used to invoke messages orderly
- */
- jsLoader.MessageQueue=(function() {
- var constructor=function() {
- this._queue=[];
- this._listened=null;
- }
- Util.extend(constructor.prototype,{
- /**
- * 队列监听器,识别队列中是否有可以产生的消息
- */
- _listen:function() {
- var _this=this;
- if (this._listened) {
- if (this._queue.length==0) {
- setTimeout(function() {
- _this._listen()
- },500);
- } else {
- this.inform();
- }
- }
- },
- /**
- * 启动监听器
- */
- startListen:function() {
- this._listened=true;
- this._listen();
- },
- /**
- * 停止监听器
- */
- stopListen:function() {
- this._listened=false;
- },
- /**
- * 通知消息队列,另起产生队列头部的一个消息
- */
- inform:function() {
- if (this._queue.length==0) {
- this._listen();
- return;
- } else {
- var message=this._queue.shift();
- var scope=this._queue.shift();
- var args=this._queue.shift();
- try {