面试题目
根据下面的配置文件
module=[
{'name':'jquery','src':'/js/lib/jquery-1.8.3.js'},
{'name':'swfobject','src':'/js/utils/swfobject.js'},
{'name':'fancybox','src':'/js/jquery/jquery.fancybox.js','require':['jquery']},
{'name':'uploadify','src':'/js/utils/uploadify.js','require':['swfobject']},
{'name':'jqform','src':'/js/jquery/jquery.form.js','require':['jquery']},
{'name':'register','src':'/js/page/reg.js','require':['jqform']},
{'name':'login','src':'/js/page/login.js','require':['fancybox','jqform']},
{'name':'upload','src':'/js/page/upload.js','require':['fancybox','jqform','uploadify']}
]
写一个函数
def getfiles(name)
返回 加载某个name指定的页面,要加载的js文件列表,有依赖的要先加载
小菜解法
此题粗看起来很简单,实则不然。
难点在于依赖模块的加载时机。假如有这样的依赖关系:A-B&C、B-C,A模块依赖B模块和C模块,同时B模块又依赖了C模块,总不能让C加载两次吧!
小菜给出的这个解法,只是一个思路,肯定有比这更好的算法,小菜觉得可以用二叉树之类的算法解决,但小菜不会呀~~~
此算法没有考虑循环依赖的情景。
代码如下:
1 /** 2 * 不考虑循环依赖 3 * @type {Function} 4 */ 5 var loadModule = (function(){ 6 /** 7 * 业务逻辑封装 8 * @type {{chainHead: {}, chainCurrent: {}, srcCache: {}, main: main, load: load, findModule: findModule}} 9 */ 10 var logics = { 11 chainHead: {}, //链表头 12 chainCurrent: {}, //链表当前节点 13 srcCache: {}, //module src 缓存 14 /** 15 * 对外接口 16 * @param modules 配置对象 17 * @param name 模块名称 18 * @returns {Array} 依赖模块列表,按照加载先后顺序排列 19 */ 20 main: function(modules, name){ 21 var nameArray = [], //模块名称列表 22 srcArray = [], //依赖模块列表 23 nameStr = "", //模块名称字符串集 24 repeatRegex = /(^| )([\w]+ ).*\2/, //模块名称去重正则 25 i = 0; 26 27 //粗略加载所有依赖模块 28 this.load(modules, name) 29 30 //构造模块名称字符串集 31 this.chainCurrent = this.chainHead; 32 while(this.chainCurrent.next){ 33 nameArray.push(this.chainCurrent.name); 34 this.chainCurrent = this.chainCurrent.next; 35 } 36 nameStr = nameArray.join(" ") + " "; //统一标准,末尾补一个空格 37 38 //依赖模块去重 39 while(repeatRegex.exec(nameStr)){ 40 nameStr = nameStr.replace(repeatRegex, function(g0, g1, g2){ 41 return g0.substring(0, (g0.length - g2.length)); 42 }); 43 } 44 nameStr = nameStr.substring(0, (nameStr.length - 1)); //去掉补充的多余空格 45 46 //依赖模块名称转换为模块路径 47 nameArray = nameStr.split(" "); 48 for(i = 0; i < nameArray.length; i++){ 49 srcArray.push(this.srcCache[nameArray[i]]); 50 } 51 52 return srcArray; 53 }, 54 /** 55 * 递归加载模块 56 * @param modules 配置对象 57 * @param name 模块名称 58 */ 59 load: function(modules, name){ 60 var node = {}, 61 module = this.findModule.call(modules, "name", name), 62 i = 0; 63 //判断模块是否存在 64 if(!module){ 65 throw Error("依赖模块 " + name +" 未找到"); 66 } 67 //构造模块依赖链表 68 node.name = name; 69 // node.src = module.src; 70 this.srcCache[name] = module.src; 71 node.next = this.chainHead; 72 this.chainHead = node; 73 //递归依赖 74 if(module.require && module.require.length){ 75 for(i = 0;i < module.require.length; i++){ 76 this.load(modules, module.require[i]); 77 } 78 } 79 }, 80 /** 81 * 根据指定属性名称和属性值查找模块 82 * @param name 属性名称 83 * @param value 属性值 84 * @returns {*} 85 */ 86 findModule: function(name, value){ 87 var array = this, 88 item = {}, 89 i = 0; 90 //遍历模块 91 for(i = 0; i < array.length; i++){ 92 item = array[i]; 93 //获取指定模块 94 if(item && item[name] === value){ 95 return item; 96 } 97 } 98 99 //找不到返回null 100 return null; 101 } 102 }; 103 104 //暴露对外接口 105 return function(){ 106 return logics.main.apply(logics, arguments); 107 }; 108 }()); 109 110 111 /** 112 * Test Usecase 113 * @type {*[]} 114 */ 115 116 var modules=[ 117 {'name':'jquery','src':'/js/lib/jquery-1.8.3.js'}, 118 {'name':'swfobject','src':'/js/utils/swfobject.js'}, 119 {'name':'fancybox','src':'/js/jquery/jquery.fancybox.js','require':['jquery']}, 120 {'name':'uploadify','src':'/js/utils/uploadify.js','require':['swfobject']}, 121 {'name':'jqform','src':'/js/jquery/jquery.form.js','require':['jquery']}, 122 {'name':'register','src':'/js/page/reg.js','require':['jqform']}, 123 {'name':'login','src':'/js/page/login.js','require':['fancybox','jqform']}, 124 {'name':'upload','src':'/js/page/upload.js','require':['fancybox','jqform','login','uploadify']} 125 ]; 126 127 console.log(loadModule(modules, "upload"));