根据配置文件加载js依赖模块

  要求:

  根据下面的配置文件

  

复制代码 代码如下:

  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加载两次吧!

  小菜给出的这个解法,只是一个思路,肯定有比这更好的算法,小菜觉得可以用二叉树之类的算法解决,但小菜不会呀~~~

  此算法没有考虑循环依赖的情景。

  代码如下:

  

复制代码 代码如下:

  /**

  * 不考虑循环依赖

  * @type {Function}

  */

  var loadModule = (function(){

  /**

  * 业务逻辑封装

  * @type {{chainHead: {}, chainCurrent: {}, srcCache: {}, main: main, load: load, findModule: findModule}}

  */

  var logics = {

  chainHead: {},     //链表头

  chainCurrent: {},  //链表当前节点

  srcCache: {},      //module src 缓存

  /**

  * 对外接口

  * @param modules  配置对象

  * @param name  模块名称

  * @returns {Array} 依赖模块列表,按照加载先后顺序排列

  */

  main: function(modules, name){

  var nameArray = [],  //模块名称列表

  srcArray = [],   //依赖模块列表

  nameStr = "",    //模块名称字符串集

  repeatRegex = /(^| )([\w]+ ).*\2/,  //模块名称去重正则

  i = 0;

  //粗略加载所有依赖模块

  this.load(modules, name)

  //构造模块名称字符串集

  this.chainCurrent = this.chainHead;

  while(this.chainCurrent.next){

  nameArray.push(this.chainCurrent.name);

  this.chainCurrent = this.chainCurrent.next;

  }

  nameStr = nameArray.join(" ") + " ";  //统一标准,末尾补一个空格

  //依赖模块去重

  while(repeatRegex.exec(nameStr)){

  nameStr = nameStr.replace(repeatRegex, function(g0, g1, g2){

  return g0.substring(0, (g0.length - g2.length));

  });

  }

  nameStr = nameStr.substring(0, (nameStr.length - 1));  //去掉补充的多余空格

  //依赖模块名称转换为模块路径

  nameArray = nameStr.split(" ");

  for(i = 0; i < nameArray.length; i++){

  srcArray.push(this.srcCache[nameArray[i]]);

  }

  return srcArray;

  },

  /**

  * 递归加载模块

  * @param modules  配置对象

  * @param name  模块名称

  */

  load: function(modules, name){

  var node = {},

  module = this.findModule.call(modules, "name", name),

  i = 0;

  //判断模块是否存在

  if(!module){

  throw Error("依赖模块 " + name +" 未找到");

  }

  //构造模块依赖链表

  node.name = name;

  //            node.src = module.src;

  this.srcCache[name] = module.src;

  node.next = this.chainHead;

  this.chainHead = node;

  //递归依赖

  if(module.require && module.require.length){

  for(i = 0;i < module.require.length; i++){

  this.load(modules, module.require[i]);

  }

  }

  },

  /**

  * 根据指定属性名称和属性值查找模块

  * @param name  属性名称

  * @param value  属性值

  * @returns {*}

  */

  findModule: function(name, value){

  var array = this,

  item = {},

  i = 0;

  //遍历模块

  for(i = 0; i < array.length; i++){

  item = array[i];

  //获取指定模块

  if(item && item[name] === value){

  return item;

  }

  }

  //找不到返回null

  return null;

  }

  };

  //暴露对外接口

  return function(){

  return logics.main.apply(logics, arguments);

  };

  }());

  /**

  * Test Usecase

  * @type {*[]}

  */

  var modules=[

  {'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','login','uploadify']}

  ];

  console.log(loadModule(modules, "upload"));