在上一篇文章中,咱們的AMD模塊加載器基本已經可以使用了,可是還不夠,由於咱們沒有容許匿名模塊,以及沒有依賴等狀況。實際上在amd的規範中規定的就是define函數的前兩個參數是可選的,當沒有id(模塊名)的時候也就意味着不會有模塊依賴於這個模塊。很顯然,咱們的define函數的每一個參數的類型是不一樣的,所以咱們須要一些函數來作類型判斷,以下:javascript
function isFun(f) {
return Object.prototype.toString.call(f).toLowerCase().indexOf('function') > -1;
}
function isArr(arr) {
return Array.isArray(arr);
}
function isStr(str) {
return typeof str === 'string';
}複製代碼
將這些類型判斷函數運用在define函數,判斷這個模塊是否有依賴,是否爲匿名模塊,這是一個比較簡單的工做,修改define函數以下:java
function define(name, deps, callback) {
if(!isStr(name)) {
callback = deps;
deps = name;
name = null;
}
if(!isArr(deps)) {
callback = deps;
deps = [];
}
if(moduleMap[name]) {
name=moduleMap[name]
}
name = replaceName(name);
deps = deps.map(function(ele, i) {
return replaceName(ele);
});
modMap[name] = modMap[name] || {};
modMap[name].deps = deps;
modMap[name].status = 'loaded';
modMap[name].callback = callback;
modMap[name].oncomplete = modMap[name].oncomplete || [];
}複製代碼
進行一次測試,不過在測試以前,咱們須要知道的是,咱們將匿名模塊的name修改成了null,然後面有一個replaceName方法是作name替換的,這裏沒有判斷name是否爲null的狀況,所以須要在開頭作一次判斷,增長以下代碼:node
function replaceName(name) {
if(name===null) {
return name;
}
// ......
}複製代碼
測試代碼以下:git
loadjs.config({
baseUrl:'./static',
paths: {
app: './app'
}
});
loadjs.define('cc',['a'], function(a) {
console.log(1);
console.log(a.add(1,2));
});
loadjs.define('ab', function() {
console.log('ab');
});
loadjs.define(function() {
console.log('unknow');
});
loadjs.use(['ab','cc'],function() {
console.log('main');
});複製代碼
測試結果以下:
github
說明正確。此時咱們的一個簡單的amd模塊加載器就這樣寫完了,刪除console增長註釋就能夠比較好的使用了,最後整理一下代碼以下:app
(function(root){
var modMap = {};
var moduleMap = {};
var cfg = {
baseUrl: location.href.replace(/(\/)[^\/]+$/g, function(s, s1){
return s1
}),
path: {
}
};
// 完整網址
var fullPathRegExp = /^[(https?\:\/\/) | (file\:\/\/\/)]/;
// 局對路徑
var absoPathRegExp = /^\//;
// 以./開頭的相對路徑
var relaPathRegExp = /^\.\//;
// 以../開頭的的相對路徑
var relaPathBackRegExp = /^\.\.\//;
function isFun(f) {
return Object.prototype.toString.call(f).toLowerCase().indexOf('function') > -1;
}
function isArr(arr) {
return Array.isArray(arr);
}
function isStr(str) {
return typeof str === 'string';
}
function merge(obj1, obj2) {
if(obj1 && obj2) {
for(var key in obj2) {
obj1[key] = obj2[key]
}
}
}
function outputPath(baseUrl, path) {
if (relaPathRegExp.test(path)) {
if(/\.\.\//g.test(path)) {
var pathArr = baseUrl.split('/');
var backPath = path.match(/\.\.\//g);
var joinPath = path.replace(/[(^\./)|(\.\.\/)]+/g, '');
var num = pathArr.length - backPath.length;
return pathArr.splice(0, num).join('/').replace(/\/$/g, '') + '/' +joinPath;
} else {
return baseUrl.replace(/\/$/g, '') + '/' + path.replace(/[(^\./)]+/g, '');
}
} else if (fullPathRegExp.test(path)) {
return path;
} else if (absoPathRegExp.test(path)) {
return baseUrl.replace(/\/$/g, '') + path;
} else {
return baseUrl.replace(/\/$/g, '') + '/' + path;
}
}
function replaceName(name) {
if(name===null) {
return name;
}
if(fullPathRegExp.test(name) || absoPathRegExp.test(name) || relaPathRegExp.test(name) || relaPathBackRegExp.test(name)) {
return outputPath(cfg.baseUrl, name);
} else {
var prefix = name.split('/')[0] || name;
if(cfg.paths[prefix]) {
if(name.split('/').length === 0) {
return cfg.paths[prefix];
} else {;
var endPath = name.split('/').slice(1).join('/');
return outputPath(cfg.paths[prefix], endPath);
}
} else {
return outputPath(cfg.baseUrl, name);
}
}
}
function fixUrl(name) {
return name.split('/')[name.split('/').length-1]
}
function config(obj) {
if(obj){
if(obj.baseUrl) {
obj.baseUrl = outputPath(cfg.baseUrl, obj.baseUrl);
}
if(obj.paths) {
var base = obj.baseUrl || cfg.baseUrl;
for(var key in obj.paths) {
obj.paths[key] = outputPath(base, obj.paths[key]);
}
}
merge(cfg, obj);
}
}
function use(deps, callback) {
if(deps.length === 0) {
callback();
}
var depsLength = deps.length;
var params = [];
for(var i = 0; i < deps.length; i++) {
moduleMap[fixUrl(deps[i])] = deps[i];
deps[i] = replaceName(deps[i]);
(function(j){
loadMod(deps[j], function(param) {
depsLength--;
params[j] = param;
if(depsLength === 0) {
callback.apply(null, params);
}
})
})(i)
}
}
function loadMod(name, callback) {
/*模塊還未定義*/
if(!modMap[name]) {
modMap[name] = {
status: 'loading',
oncomplete: []
};
loadscript(name, function() {
use(modMap[name].deps, function() {
execMod(name, callback, Array.prototype.slice.call(arguments, 0));
})
});
} else if(modMap[name].status === 'loading') {
// 模塊正在加載
modMap[name].oncomplete.push(callback);
} else if (!modMap[name].exports){
//模塊還未執行完
use(modMap[name].deps, function() {
execMod(name, callback, Array.prototype.slice.call(arguments, 0));
})
}else {
callback(modMap[name].exports);
}
}
function execMod(name, callback, params) {
var exp = modMap[name].callback.apply(null, params);
modMap[name].exports = exp;
callback(exp);
execComplete(name);
}
function execComplete(name) {
for(var i = 0; i < modMap[name].oncomplete.length; i++) {
modMap[name].oncomplete[i](modMap[name].exports);
}
}
function loadscript(name, callback) {
var doc = document;
var node = doc.createElement('script');
node.charset = 'utf-8';
node.src = name + '.js';
/*爲每一個模塊添加一個隨機id*/
node.id = 'loadjs-js-' + (Math.random() * 100).toFixed(3);
doc.body.appendChild(node);
node.onload = function() {
callback();
}
}
function define(name, deps, callback) {
/*匿名模塊*/
if(!isStr(name)) {
callback = deps;
deps = name;
name = null;
}
/*沒有依賴*/
if(!isArr(deps)) {
callback = deps;
deps = [];
}
if(moduleMap[name]) {
name=moduleMap[name]
}
name = replaceName(name);
/*對每一個依賴名進行路徑替換*/
deps = deps.map(function(ele, i) {
return replaceName(ele);
});
modMap[name] = modMap[name] || {};
modMap[name].deps = deps;
modMap[name].status = 'loaded';
modMap[name].callback = callback;
modMap[name].oncomplete = modMap[name].oncomplete || [];
}
var loadjs = {
define: define,
use: use,
config: config
};
root.define = define;
root.loadjs = loadjs;
root.modMap = modMap;
})(window);複製代碼