直接贴代码,马上下班了,有时间再整理讲解一下
1 (function(win, doc){ 2 3 var c = { 4 callback:{} 5 } 6 7 function isType(type){ 8 return function(obj){ 9 return Object.prototype.toString.call(obj).toLowerCase() == "[object "+type.toLowerCase()+"]"; 10 }; 11 } 12 var isFunction = isType("function"); 13 var isObject = isType("object"); 14 var isString = isType("string"); 15 var isNumber = isType("number"); 16 var isArray = isType("array"); 17 18 var MODULES = {}; 19 var LOADINGS = {}; 20 var W3C = document.dispatchEvent; 21 var DEPS_RE = /"(?:\\"|[^"])*"|‘(?:\\‘|[^‘])*‘|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["‘])(.+?)\1\s*\)/g; 22 var HEAD = doc.getElementsByTagName("head")[0]; 23 var READY_STATE_RE = /^(?:loaded|complete|undefined)$/; 24 var method = W3C ? "onload" : "onreadystatechange"; 25 26 var STATUS = { 27 INIT:1, 28 LOADING:2, 29 LOADED:3, 30 COMPLATE:4 31 }; 32 33 var log = function(msg){ 34 if(console){ 35 console.log(msg); 36 }else{ 37 alert(msg); 38 } 39 } 40 41 function getModPath(name){ 42 if(!PATHS[name]){ 43 //console.log(name+" is undefined") 44 } 45 return name && PATHS[name]; 46 } 47 48 var inArray = function(child, array){ 49 var i=0,len = array.length; 50 for(;i<len;i++){ 51 if(child == array[i]){ 52 return true; 53 } 54 } 55 return false; 56 }; 57 58 function checkIsDone(deps){ 59 var i=0,len = deps.length,done = true; 60 for(;i<len;i++){ 61 if(!MODULES[deps[i]]){ 62 done = false; 63 } 64 } 65 return done; 66 } 67 68 69 //检查循环依赖 70 function checkCycle(mod, deps){ 71 72 var parent, bln = false; 73 if(!deps.length){ 74 return bln; 75 } 76 77 parent = mod.parent; 78 for(var j=0;j<parent.length;j++){ 79 for(var i =0,len=deps.length;i<len;i++){ 80 if(parent[j].name == deps[i] ){ 81 return bln=true; 82 } 83 } 84 if(bln){ 85 return bln; 86 }else{ 87 bln = checkCycle(parent[j],deps); 88 } 89 } 90 91 92 return bln; 93 } 94 95 function addCallback(name, fn){ 96 var aCall = c.callback[name]; 97 if(aCall) { 98 aCall.push(fn) 99 }else{ 100 c.callback[name] = [fn]; 101 } 102 } 103 104 function fireCallback(name){ 105 if(c.callback[name]){ 106 var i=0, aCall =c.callback[name],len=c.callback[name].length, mod = MODULES[name]; 107 if(aCall && len && mod) { 108 for(;i<len;i++){ 109 aCall[i](mod.exec()); 110 } 111 } 112 } 113 } 114 115 function hasCallback(name){ 116 if(c.callback[name] && c.callback[name].length){ 117 return true; 118 } 119 return false; 120 } 121 122 function loadJS(path){ 123 if(!path){ 124 return false; 125 } 126 var script = doc.createElement("script"); 127 script.src = path; 128 HEAD.insertBefore(script,HEAD.firstChild); 129 130 script[method] = function(e){ 131 if (READY_STATE_RE.test(script.readyState)) { 132 script.onload = script.onerror = script.onreadystatechange = null; 133 HEAD.removeChild(script); 134 script = null; 135 } 136 }; 137 138 return script; 139 } 140 function use(name, callback){ 141 var m = MODULES[name]; 142 if(!m){ 143 m = (MODULES[name] = new Loadmod(name)); 144 m.isLoadDeps = true; 145 addCallback(name, callback); 146 m.load(); 147 }else{ 148 addCallback(name, callback); 149 m.checkDeps(); 150 } 151 152 } 153 function parseDependencies(code) { 154 var ret = []; 155 code.replace(DEPS_RE, function(m, m1, m2) { 156 if (m2) { 157 ret.push(m2); 158 } 159 }); 160 return ret; 161 } 162 function Loadmod(name){ 163 this.name = name; 164 this.status = STATUS.INIT; 165 this.deps = []; 166 this.isLoadDeps = false; 167 this.exports = {}; 168 this._nw = 0; 169 this.parent = []; 170 } 171 Loadmod.prototype = { 172 173 load:function(){ 174 175 if(this.status == STATUS.INIT){ 176 177 loadJS(getModPath(this.name)); 178 this.status = STATUS.LOADING; 179 } 180 181 }, 182 onload:function(){ 183 this.status = STATUS.LOADED; 184 if(this.isLoadDeps){ 185 this.checkDeps(); 186 } 187 }, 188 setnw:function(){ 189 if(this._nw ==0) return; 190 if((--this._nw) == 0){ 191 this.complate(); 192 } 193 }, 194 setDeps:function(deps){ 195 this.deps = deps; 196 this._nw = deps.length; 197 }, 198 checkDeps:function(){ 199 if(this.factory){ 200 var deps = parseDependencies(this.factory.toString()); 201 if(deps.length){ 202 this.setDeps(deps); 203 this.loadDeps(); 204 }else{ 205 this.complate(); 206 } 207 }else{ 208 this.load(); 209 } 210 }, 211 loadDeps:function(){ 212 var self = this; 213 if(this.deps.length){ 214 for(var i=0;i<this.deps.length;i++){ 215 var m = MODULES[this.deps[i]]; 216 if(m){ 217 m.setParent(this); 218 if(m.status == STATUS.COMPLATE){ 219 this.setnw(); 220 }else{ 221 m.checkDeps(); 222 } 223 }else{ 224 m = (MODULES[this.deps[i]] = new Loadmod(this.deps[i])); 225 m.isLoadDeps = true; 226 m.setParent(this); 227 m.load(); 228 } 229 } 230 }else{ 231 this.complate(); 232 } 233 }, 234 setParent:function(parent){ 235 this.parent.push(parent); 236 }, 237 getDeps:function(){ 238 return this.deps.length ? this.deps : parseDependencies(this.factory.toString()); 239 }, 240 complate:function(){ 241 this.status = STATUS.COMPLATE; 242 this.updateParent(); 243 this.doCallback(); 244 }, 245 updateParent:function(){ 246 if(this.parent.length){ 247 var i=0,len = this.parent.length; 248 if(this.parent[i].status!= STATUS.COMPLATE){ 249 for(;i<len;i++){ 250 this.parent[i].setnw(); 251 } 252 } 253 } 254 }, 255 exec:function(){ 256 257 if(!this.cache){ 258 var exports = {}, module = this; 259 exports = this.factory(require,module.exports = {} ,module) || module.exports; 260 module.cache = exports; 261 } 262 return this.cache; 263 }, 264 doCallback:function(){ 265 fireCallback(this.name); 266 } 267 }; 268 269 function require(name){ 270 271 return MODULES[name].exec(); 272 } 273 274 function define(name, fn){ 275 if(!isString(name)){ 276 return false; 277 } 278 var m = MODULES[name] || (MODULES[name] = new Loadmod(name)); 279 m.factory = fn; 280 var time = new Date().getTime(); 281 if(checkCycle(m, m.getDeps())){ 282 log(name+" 存在循环依赖关系"); 283 return; 284 } 285 console.log(m.name+"|"+(new Date().getTime()-time)); 286 m.onload(); 287 } 288 289 define.cmd = {}; 290 win.useJs = use; 291 win.define = define; 292 293 })(window, document)
原文:http://www.cnblogs.com/yyqhlw/p/3579294.html