近最研究加载信息,稍微总结一下,以后继续补充:
久很没有写博客了,一来是任务比较忙,二来主要是得觉没什么可写。当然,自己的惰懒也是可不推辞的任责。
近最有点空余的时光,就看了一下AMD模块加载。关于它的定义和优缺点就不分析了,园子里大把。相信大家也都道知。主要说一下加载器的理原以及在开辟进程当中碰到的一些坑。当然由于加载器不是标准的一部分,所以实现方法也各不相同。我所用的方法也不是最优的,只是用来作当学习而已。
【模块加载器理原】
1.开始
2.通过模块名解析出模块息信,以及计算出URL。
3.通过建创SCRIPT的情势把模块加载到页面中。(当然也可以采取其它方法,如XHR。 IFRAME等等)
4.判断被加载的本脚,如果现发它有依附就去加载依附模块。如果不依附其它模块,就直接执行factory方法。
5.等部全本脚都被加载毕完就执行加载实现以后的回调函数。
【实现的进程】
在弄懂了个整加载器的任务理原以后,就开始详细的码编进程。最开始,我使用了moduleCache,modules,moduleLoads这三个象对。分离记载加载中须要用到的息信。首先我把个整加载的息信存储到moduleCache中。其中的构结大致如下。
moduleCache[uuid] = { uuid : uuid, //随即生成的id deps : deps, //此次加载需须要加载的模块 args : args, //回调函数须要的方法 callback : callback //此次加载实现后须要调用的回调函数 }
modules[modname] = { name : name, //模块名字 url : url, //模块的url state : state //模块的态状
}
然后开始实现加载的模块依附别的模块,一开始的做法是当现发加载的模块存在依附的时候,就从新调用require方法去加载模块须要的模块。 然而这样就造成了一个问题。就是等部全的被依附的模块加载毕完以后,按照面上的程流执行毕完。没法知告须要依附的那个模块它依附的模块都被加载毕完了。可能这么说不是太直观,看一下在现变量里存储的息信就然了目一了。
//加载hello模块,hello模块又依附test模块。 moduleCache = { cb100001 : { uuid : cb10001 deps: {hello} args:[hello] callback : callback } , cb100002:{ uuid:cb10002, deps:{test} args:[test] callback : callback } } modules : { hello : { name:hello, url:url, state : 1, exports : {} }, test: { name:test url:url state:2 exports:{} } }
var moduledeps = { 'hello' : ['test'] 'test' : ['module1','module2'] }
构结如下。
modules = { url1 : { name: hello, url : url1, state : 1 exports : {} }, url2 : { name: test, url : url2, state : 2 exports : {} } } moduleCache : { cbi10001 : { state: 1, uuid : cbi10001, factory : callback, args : [url1],l deps :{url1:'lynx'} }, url1 : { state: 1, uuid : url1, factory : callback, args : [url2], deps :{url2:'lynx'} } }
![信息和方法](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
![信息和方法](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
1 View Code 2 3 (function(win, undefined){ 4 win = win || window; 5 var doc = win.document || document, 6 head = doc.head || doc.getElementsByTagName("head")[0]; 7 hasOwn = Object.prototype.hasOwnProperty, 8 slice = Array.prototype.slice, 9 basePath = (function(nodes){ 10 var node = nodes[nodes.length - 1], 11 url = (node.hasAttribute ? node.src : node.getAttribute("src", 4)).replace(/[?#].*/, ""); 12 return url.slice(0, url.lastIndexOf('/') + 1); 13 }(doc.getElementsByTagName('script'))); 14 15 function lynxcat(){ 16 17 } 18 lynxcat.prototype = { 19 constructor : lynxcat, 20 init : function(){ 21 22 } 23 } 24 lynxcat.prototype.init.prototype = lynxcat.prototype; 25 26 /** 27 * mix 28 * @param {Object} target 目标象对 29 * @param {Object} source 源象对 30 * @return {Object} 目标象对 31 */ 32 lynxcat.mix = function(target, source){ 33 if( !target || !source ) return; 34 var args = slice.call(arguments), i = 1, override = typeof args[args.length - 1] === "boolean" ? args.pop() : true, prop; 35 while ((source = args[i++])) { 36 for (prop in source) { 37 if (hasOwn.call(source, prop) && (override || !(prop in target))) { 38 target[prop] = source[prop]; 39 } 40 } 41 } 42 return target; 43 }; 44 45 lynxcat.mix(lynxcat, { 46 modules : {}, 47 moduleCache : {}, 48 loadings : [], 49 50 /** 51 * parse module 52 * @param {String} id 模块名 53 * @param {String} basePath 基础路径 54 * @return {Array} 55 */ 56 parseModule : function(id, basePath){ 57 var url, result, ret, dir, paths, i, len, ext, modname, protocol = /^(\w+\d?:\/\/[\w\.-]+)(\/(.*))?/; 58 if(result = protocol.exec(id)){ 59 url = id; 60 paths = result[3] ? result[3].split('/') : []; 61 }else{ 62 result = protocol.exec(basePath); 63 url = result[1]; 64 paths = result[3] ? result[3].split('/') : []; 65 modules = id.split('/'); 66 paths.pop(); 67 for(i = 0, len = modules.length; i < len; i++){ 68 dir = modules[i]; 69 if(dir == '..'){ 70 paths.pop(); 71 }else if(dir !== '.'){ 72 paths.push(dir); 73 } 74 } 75 url = url + '/' + paths.join('/'); 76 } 77 modname = paths[paths.length - 1]; 78 ext = modname.slice(modname.lastIndexOf('.')); 79 if(ext != '.js'){ 80 url = url + '.js'; 81 }else{ 82 modname = modname.slice(0, modname.lastIndexOf('.')); 83 } 84 if(modname == ''){ 85 modname = url; 86 } 87 return [modname, url] 88 }, 89 90 /** 91 * get uuid 92 * @param {String} prefix 93 * @return {String} uuid 94 */ 95 guid : function(prefix){ 96 prefix = prefix || ''; 97 return prefix + (+new Date()) + String(Math.random()).slice(-8); 98 }, 99 100 /** 101 * error 102 * @param {String} str 103 */ 104 error : function(str){ 105 throw new Error(str); 106 } 107 }); 108 109 110 //================================ 模块加载 ================================ 111 /** 112 * 模块加载方法 113 * @param {String|Array} ids 须要加载的模块 114 * @param {Function} callback 加载实现以后的回调 115 * @param {String} parent 父路径 116 */ 117 win.require = lynxcat.require = function(ids, callback, parent){ 118 ids = typeof ids === 'string' ? [ids] : ids; 119 var i = 0, len = ids.length, flag = true, uuid = parent || lynxcat.guid('cb_'), path = parent || basePath, 120 modules = lynxcat.modules, moduleCache = lynxcat.moduleCache, 121 args = [], deps = {}, id, result; 122 for(; i < len; i++){ 123 id = ids[i]; 124 result = lynxcat.parseModule(id, path); 125 126 if(!modules[result[1]]){ 127 modules[result[1]] = { 128 name : result[0], 129 url : result[1], 130 state : 0, 131 exports : {} 132 } 133 flag = false; 134 }else if(modules[result[1]].state != 2){ 135 flag = false; 136 } 137 if(!deps[result[1]]){ 138 if(checkCircularDeps(uuid, result[1])){ 139 lynxcat.error('模块[url:'+ uuid +']与模块[url:'+ result[1] +']循环依附'); 140 } 141 deps[result[1]] = 'lynxcat'; 142 args.push(result[1]); 143 } 144 lynxcat.loadJS(result[1]); 145 } 146 147 moduleCache[uuid] = { 148 uuid : uuid, 149 factory : callback, 150 args : args, 151 deps : deps, 152 state : 1 153 } 154 155 if(flag){ 156 fireFactory(uuid); 157 return checkLoadReady(); 158 } 159 }; 160 require.amd = lynxcat.modules; 161 162 /** 163 * @param {String} id 模块名 164 * @param {String|Array} [dependencies] 依附列表 165 * @param {Function} factory 工厂方法 166 */ 167 win.define = function(id, dependencies, factory){ 168 if((typeof id === 'array' || typeof id === 'string') && typeof dependencies === 'function'){ 169 factory = dependencies; 170 dependencies = []; 171 }else if (typeof id == 'function'){ 172 factory = id; 173 dependencies = []; 174 } 175 id = lynxcat.getCurrentScript(); 176 if(!id){ 177 lynxcat.loadings.push(function(id){ 178 require(dependencies, factory, id); 179 }); 180 }else{ 181 require(dependencies, factory, id); 182 } 183 } 184 185 /** 186 * fire factory 187 * @param {String} uuid 188 */ 189 function fireFactory(uuid){ 190 var moduleCache = lynxcat.moduleCache, modules = lynxcat.modules, 191 data = moduleCache[uuid], deps = data.args, result, 192 i = 0, len = deps.length, args = []; 193 for(; i < len; i++){ 194 args.push(modules[deps[i]].exports) 195 } 196 result = data.factory.apply(null, args); 197 if(modules[uuid]){ 198 modules[uuid].state = 2; 199 modules[uuid].exports = result; 200 delete moduleCache[uuid]; 201 }else{ 202 delete lynxcat.moduleCache; 203 } 204 return result; 205 } 206 207 /** 208 * 检测否是部全加载毕完 209 */ 210 function checkLoadReady(){ 211 var moduleCache = lynxcat.moduleCache, modules = lynxcat.modules, 212 i, data, prop, deps, mod; 213 loop: for (prop in moduleCache) { 214 data = moduleCache[prop]; 215 deps = data.args; 216 for(i = 0; mod = deps[i]; i++){ 217 if(hasOwn.call(modules, mod) && modules[mod].state != 2){ 218 continue loop; 219 } 220 } 221 if(data.state != 2){ 222 fireFactory(prop); 223 checkLoadReady(); 224 } 225 } 226 } 227 228 /** 229 * 检测循环依附 230 * @param {String} id 231 * @param {Array} dependencie 232 */ 233 function checkCircularDeps(id, dependencie){ 234 var moduleCache = lynxcat.moduleCache, depslist = moduleCache[dependencie] ? moduleCache[dependencie].deps : {}, prop; 235 for(prop in depslist){ 236 if(hasOwn.call(depslist, prop) && prop === id){ 237 return true; 238 } 239 } 240 return false; 241 } 242 243 lynxcat.mix(lynxcat, { 244 /** 245 * 加载JS文件 246 * @param {String} url 247 */ 248 loadJS : function(url){ 249 var node = doc.createElement("script"); 250 node[node.onreadystatechange ? 'onreadystatechange' : 'onload'] = function(){ 251 if(!node.onreadystatechange || /loaded|complete/i.test(node.readyState)){ 252 var fn = lynxcat.loadings.pop(); 253 fn && fn.call(null, node.src); 254 node.onload = node.onreadystatechange = node.onerror = null; 255 head.removeChild(node); 256 } 257 } 258 node.onerror = function(){ 259 lynxcat.error('模块[url:'+ node.src +']加载失败'); 260 node.onload = node.onreadystatechange = node.onerror = null; 261 head.removeChild(node); 262 } 263 node.src = url; 264 head.insertBefore(node, head.firstChild); 265 }, 266 267 /** 268 * get current script [此方法来自司徒正美的博客] 269 * @return {String} 270 */ 271 getCurrentScript : function(){ 272 //取得正在解析的script节点 273 if (doc.currentScript) { //firefox 4+ 274 return doc.currentScript.src; 275 } 276 // 参考 https://github.com/samyk/jiagra/blob/master/jiagra.js 277 var stack; 278 try { 279 a.b.c(); //强制报错,以便捕获e.stack 280 } catch (e) { //safari的错误象对只有line,sourceId,sourceURL 281 stack = e.stack; 282 if (!stack && window.opera) { 283 //opera 9没有e.stack,但有e.Backtrace,但不能直接取得,须要对e象对转字符串进行抽取 284 stack = (String(e).match(/of linked script \S+/g) || []).join(" "); 285 } 286 } 287 if (stack) { 288 /**e.stack最后一行在部全支持的浏览器大致如下: 289 *chrome23: 290 * at http://113.93.50.63/data.js:4:1 291 *firefox17: 292 *@http://113.93.50.63/query.js:4 293 *opera12:http://www.oldapps.com/opera.php?system=Windows_XP 294 *@http://113.93.50.63/data.js:4 295 *IE10: 296 * at Global code (http://113.93.50.63/data.js:4:1) 297 */ 298 stack = stack.split(/[@ ]/g).pop(); //取得最后一行,最后一个空格或@以后的部分 299 stack = stack[0] === "(" ? stack.slice(1, -1) : stack; 300 return stack.replace(/(:\d+)?:\d+$/i, ""); //去掉行号与或许存在的出错字符起始位置 301 } 302 var nodes = head.getElementsByTagName("script"); //只在head标签中寻找 303 for (var i = 0, node; node = nodes[i++]; ) { 304 if (node.readyState === "interactive") { 305 return node.src; 306 } 307 } 308 } 309 }); 310 win.lynxcat = lynxcat; 311 }(window));
使用方法
//hello.js文件 define('hello', function(){ return {world : 'hello, world!'}; }); //主文件 lynxcat.require('hello',function(hello){ console.log(hello.world); //hello, world!; }); //hello.js 有依附的情况 //hello.js文件 define('hello', 'test',function(test){ return {world : 'hello, world!' + test}; }); //test.js文件 define('test', function(){ return 'this is test'; }); //主文件 lynxcat.require('hello',function(hello){ console.log(hello.world); //hello, world!this is test; });
文章结束给大家分享下程序员的一些笑话语录: IT业众生相
第一级:神人,天资过人而又是技术狂热者同时还拥有过人的商业头脑,高瞻远瞩,技术过人,大器也。如丁磊,求伯君。
第二级:高人,有天赋,技术过人但没有过人的商业头脑,通常此类人不是顶尖黑客就是技术总监之流。
第三级:牛人,技术精湛,熟悉行业知识,敢于创新,有自己的公司和软件产品。
第四级:工头,技术精湛,有领导团队的能力,此类人大公司项目经理居多。
第五级:技术工人,技术精湛,熟悉行业知识但领导能力欠加,此类人大多为系分人员或资深程序员,基本上桀骜不逊,自视清高,不愿于一般技术人员为伍,在论坛上基本以高手面目出现。
第六级:熟练工人,技术有广度无深度,喜欢钻研但浅尝辄止。此类人大多为老程序员,其中一部分喜欢利用工具去查找网上有漏洞的服务器,干点坏事以获取成绩感。如果心情好,在论坛上他们会回答菜鸟的大部分问题。此级别为软件业苦力的重要组成部分。
第七级:工人,某些技术较熟练但缺乏深度和广度,此类人大多为程序员级别,经常在论坛上提问偶尔也回答菜鸟的问题。为软件产业苦力的主要组成部分。
第八级:菜鸟,入门时间不长,在论坛上会反复提问很初级的问题,有一种唐僧的精神。虽然招人烦但基本很可爱。只要认真钻研,一两年后就能升级到上一层。
第九级:大忽悠,利用中国教育的弊病,顶着一顶高学历的帽子,在小公司里混个软件部经理,设计不行,代码不行,只会胡乱支配下属,拍领导马屁,在领导面前胡吹海侃,把自己打扮成技术高手的模样。把勾心斗角的办公室文化引入技术部门,实在龌龊!
第十级:驴或傻X,会写SELECT语句就说自己精通ORALCE,连寄存器有几种都不知道就说自己懂汇编,建议全部送到日本当IT产业工人,挣了日本人的钱还严重打击日本的软件业!