开坑!JavaScript AMD模块的设计与实现

开个坑,慢慢学习总结JavaScript的AMD规范

先把自己写的一个简易实现放上来,然后慢慢从0開始解说一下AMD,以及怎样自己动手实现一个AMD

(function(w, DOC) {
	var _$ = w.$;
	var head = DOC.head || DOC.getElementsByTagName('head')[0];
	var html = DOC.documentElement;
	var W3C = DOC.dispatchEvent;
	var basePath = getCurrentScript(DOC);
	basePath = basePath.substring(0, basePath.lastIndexOf('/') + 1);
	var hasOwn = Object.prototype.hasOwnProperty;
	var toString = Object.prototype.toString;
	var class2type = {
		
	};

	var noop = function() {};

	var AS = function() {
		if (this == w) {
			return new AS(arguments);
		} else {
			console.info(this);
		}
	};
	AS.prototype = {
		constructor: AS
	};
	AS.fn = AS.prototype;
	AS.extend = function() {
		var args = arguments;
		if (args.length === 2) {
			var target = args[0];
			var source = args[1];
			for(var attr in source) {
				target[attr] = source[attr]; 
			};
		} else {
			for(var i = 0, len = args.length; i < len; i++) {
				var obj = args[i];
				for(var attr in obj) {
					AS[attr] = obj[attr]; 
				};
			};
		};
	};

	/*AMD模块開始*/
	var modules = {};

	function getEmptyModule() {
		return {
			deps: [],
			offers: [],
			state: 0,
			factory: noop
		};
	};

	function getCurrentScript(DOC) {
		if (DOC.currentScript) {
			return DOC.currentScript.src; //FF,Chrome
		};
		var stack;
		try {
			a.b.c();
		} catch (e) {
			stack = e.stack; // 利用错误异常立马抛出高速获得当前载入文件路径
			if (!stack && window.opera) {
				stack = String(e);
				if (!stack && window.opera) {
					stack = (String(e).match(/of linked script \S+/g) || []).join(" ");
				}
			};
		};
		if (stack) {
			stack = stack.split(/[@ ]/g).pop(); //取得最后一行,最后一个空格或@之后的部分
			stack = stack[0] === "(" ? stack.slice(1, -1) : stack;
			return stack.replace(/(:\d+)?:\d+$/i, ""); //去掉行号与也许存在的出错字符起始位置
		}
		// IE
		var nodes = head.getElementsByTagName("script");
		for (var i = 0, node; node = nodes[i++];) {
			if (node.readyState === 'interactive') {
				return node.src;
			};
		};
	}

	function getModuleName(url) {
		return url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.js'));
	}

	function require(list, factory) {
		var loaded = 0;
		var requires = list.length;
		var args = [];
		var id = getCurrentScript(DOC);
		console.info('载入'+getModuleName(id)+'模块须要依赖'+ list+'模块')
		var module = modules[id] ? modules[id] : getEmptyModule();
		module['factory'] = factory;
		modules[id] = module;

		for (var i = 0; i < list.length; i++) {
			var depId = basePath + list[i] + '.js';
			modules[depId] = modules[depId] ? modules[depId] : getEmptyModule();
			module['deps'].push(depId);
			if (modules[depId] && modules[depId].state === 2) {
				console.info(getModuleName(depId)+'依赖已提前载入完成'); 
				modules[depId].offers.push(id);
				loaded++;
				args.push(modules[depId].exports);
				//此依赖模块正在被载入中
			} else if (modules[depId] && modules[depId].state === 1) {
				console.info(getModuleName(depId)+'依赖正在载入中'); 
				modules[depId].offers.push(id);
				//此依赖模块从未载入过
			} else {
				//loadJS载入模块js,模块js调用define,define又会require载入此模块须要的依赖
				//从而函数进入递归追踪并载入此模块全部直接和间接依赖
				console.info('第一次载入依赖模块' + getModuleName(depId)); 
				modules[depId].offers.push(id);
				loadJS(depId);
			};
		};
		if (loaded === requires) {
			module.factory.apply(null, args);
			module.state = 2;
		};
	};
	window.require = AS.require = require;

	function loadJS(url) {
		var module = modules[url];
		var node = DOC.createElement('script');
		node.onload = node.onreadystatechange = function() {
			if (/loaded|complete/i.test(node.readyState) || !node.readyState) {

			};
		};
		node.src = url;
		head.insertBefore(node, head.firstChild);
		module.state = 1;
		//console.info('正在载入模块'+getModuleName(url));
	};

	function findFishedModules(url) {
		var ret = [];
		var deped = modules[url];
		var moduleIds = deped.offers;
		for (var i = 0; i < moduleIds.length; i++) {
			var id = moduleIds[i];
			var deps = modules[id].deps;
			var flag = false;
			for (var j = 0; j < deps.length; j++) {
				var dep = modules[deps[j]];
				if (dep.state == 2 || deps[j] == url) {
					flag = true;
				};
				if (dep.state != 2) {
					flag = false;
					break;
				};
			};
			if (flag) {
				//console.info('模块' + getModuleName(id) + '已可用');
				ret.push(id);
			};
		};
		return ret;
	};

	function fireFactory(root) {
		console.info('查找依赖于'+getModuleName(root)+'模块的模块安装情况');
		var finshedIds = findFishedModules(root);
		for (var i = 0; i < finshedIds.length; i++) {
			var module = modules[finshedIds[i]];
			console.info(getModuleName(finshedIds[i])+'模块全部依赖已经能够安装完成,能够运行回调函数');
			var exports = module.factory.apply(null, getDepsModules(module.deps));
			module.state = 2;
			if (exports) {
				module.exports = exports;
				console.info('导入'+getModuleName(finshedIds[i])+'模块');
			} else {
				break;
			};
			fireFactory(finshedIds[i]);
		};
	};

	function getDepsModules(deps) {
		var args = [];
		for (var i = 0; i < deps.length; i++) {
			var dep = deps[i];
			args.push(modules[dep].exports);
		};
		return args;
	};

	function define(deps, factory) {
		var id = getCurrentScript(DOC);
		//假设此模块没有依赖马上安装该模块
		if (deps.length == 0) {
			modules[id].exports = factory.apply(null);
			modules[id].state = 2;
			//从依赖的叶子一直往上找一系列的触发安装
			console.info('模块'+getModuleName(id)+'载入完成');
			fireFactory(id);
			return;
		};
		require(deps, function() {
			var _deps = [];
			for (var i = 0; i < deps.length; i++) {
				_deps.push(basePath + deps[i] + '.js');
			};
			modules[id].exports = factory.apply(null, getDepsModules(_deps));
			modules[id].state = 2;
			console.info('模块'+getModuleName(id)+'载入完成');
			fireFactory(id);
		});
	};
	window.define = AS.define = define;
	/*AMD模块结束*/

	// 暴露AS函数
	w.$ = w.AS = AS;

})(self, self.document);


posted on 2017-05-03 18:10  ljbguanli  阅读(118)  评论(0编辑  收藏  举报