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

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

先把自己写的一个简易实现放上来,然后慢慢从0开始讲解一下AMD,已经如何自己动手实现一个AMD

/*AMD*/
var DOC = window.document;
var head = DOC.head || DOC.getElementsByTagName('head')[0];
var basePath = getCurrentScript(DOC);
basePath = basePath.substring(0, basePath.lastIndexOf('/')+1);

noop = function(){};
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);
	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) {
			modules[depId].offers.push(id);
			loaded++;
			args.push(modules[depId].exports);
		//此依赖模块正在被加载中
		} else if(modules[depId] && modules[depId].state === 1) {
			modules[depId].offers.push(id);
		//此依赖模块从未加载过
		} else {
			//loadJS加载模块js,模块js调用define,define又会require加载此模块需要的依赖
			//从而函数进入递归追踪并加载此模块所有直接和间接依赖
			modules[depId].offers.push(id);
			loadJS(depId);
		};
	};
	if (loaded === requires) {
		module.factory.apply(null, args)
	};
};

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);
};

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) {
	var finshedIds = findFishedModules(root);
	for (var i = 0; i < finshedIds.length; i++) {
		var module = modules[finshedIds[i]];
		module.factory.apply(null, getDepsModules(module.deps));
		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;
		//从依赖的叶子一直往上找一系列的触发安装
		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;
	});
};

开坑!JavaScript AMD模块的设计与实现,布布扣,bubuko.com

时间: 2024-12-26 13:53:31

开坑!JavaScript AMD模块的设计与实现的相关文章

JavaScript AMD 模块加载器原理与实现

关于前端模块化,玉伯在其博文 前端模块化开发的价值 中有论述,有兴趣的同学可以去阅读一下. 1. 模块加载器 模块加载器目前比较流行的有 Requirejs 和 Seajs.前者遵循 AMD规范,后者遵循 CMD规范.前者的规范产出比较适合于浏览器异步环境的习惯,后者的规范产出对于写过 nodejs 的同学来说是比较爽的.关于两者的比较,有兴趣的同学请参看玉伯在知乎的回答 AMD和CMD的区别有哪些.本文希望能按照 AMD 规范来简单实现自己的一个模块加载器,以此来搞清楚模块加载器的工作原理.

Webpack - CommonJs &amp; AMD 模块打包器

Webpack 是一个 CommonJs & AMD 模块打包器.可以把你的 JavaScript 代码分离为多个包,在需要的时候进行加载,支持预处理文件,例如 json, jade, coffee, css, less 等等. 官方网站      GitHub 您可能感兴趣的相关文章 网站开发中很有用的 jQuery 效果[附源码] 分享35个让人惊讶的 CSS3 动画效果演示 十分惊艳的8个 HTML5 & JavaScript 特效 Web 开发中很实用的10个效果[源码下载] 12

CozyRSS开发记录0-RSS阅读器开坑

CozyRSS开发记录0-RSS阅读器开坑 1.RSS RSS,全名是Really Simple Syndication,简易信息聚合. 关于RSS相关的介绍,网上可以很容易的找到.RSS阅读器是我几乎每天都用到的一个工具类软件,他的优点是: A.没有广告,只关注内容: B.可以轻松快捷的关注到一批博客和新闻网站的更新. RSS本身涉及到的技术点: A.XML,RSS使用XML来编写,它是XML的子集: B.HTTP,RSS的内容源通常是使用HTTP协议来提供的. 2.CozyRSS的目标 初步

JavaScript中模块“写法”

在JavaScript模块到底是什么 event = function() { // do more return { bind: function() {}, unbind: function() {}, trigger: function() {} }; }();这能代表"模块"吗?这就是一个JS对象啊,以为有多么深奥. 是的,JavaScript中模块多数时候被实现为一个对象.这么看来,多数时候我们都写过"模块"(但没有在整个项目中应用模块化思想).或许每个人

数据库设计之问卷模块的设计2

数据库设计之问卷模块的设计1 2016/07/13更新 整个实习已经告一段落了,其实整个问卷模块的数据库设计,在6月初基本上已经最终敲定并实现出来了. 本次的总结分为两个部分. 一.对之前提出的几个遗留问题,解决了的做一个说明 二.最终总结一下,问卷模块的核心:问卷.试题.选项几个实体之间的关系如何表示,如实记录之中的心路历程,以及遇到过的一些坑. 一.之前遗留问题 1.结果试题标签 这个结果标签其实不是难点,到时候只需要记录获得的结果标签的id即可. 一次问卷的评价当然可以获得,若干个结果标签

javascript 异步模块加载 简易实现

在javascript是没有类似java或其他语言的模块概念的,因此也不可能通过import或using等关键字来引用模块,这样造成了复杂项目中前端代码混乱,变量互相影响等. 因此在复杂项目中引入AMD的概念,AMD:全称是Asynchronous Module Definition,即异步模块加载机制.通过AMD可以不需要在页面中手动添加<script>来引用脚本,而通过定义依赖自动加载模块脚本,接下来的代码将讲解如何实现建议的AMD模块,如果需要查看比较详细的实现可以下载requirejs

Javascript的10个设计缺陷

我经常说Javascript的设计不够严谨,有很多失误. 今天的这一篇,前半部分就谈为什么会这样,后半部分将列举Javascript的10个设计缺陷. 我参考的文献主要是Douglas Crockford的专著<Javascript语言精粹>(JavaScript: The Good Parts)和Fredrik Holmstr?m的文章<我对Javascript的抱怨>(My gripes with Javascript).精心开发5年的UI前端框架! 一.为什么Javascri

重新开坑:开始自学Android编程

开坑原因 从高考后开始的这轮对于计算机知识的学习,最早的热情就是来自于学习Android Development.我们几百块钱就能买到一个不错的廉价Android设备,WLAN.GPS.NFC.4G.Screen.Camera等硬件一应俱全,利用这些硬件实现一些有趣的功能,让生活更高效,是我学习Android Development的最初动力.当然,事情不是我想象的那么简单,没有Java基础(或者说没有任何Computer Science基础)的我被搞得晕头转向,只得抱起厚厚的Core Java

JavaScript高级---门面模式设计

门面模式 两个作用: 1.简化类的接口 2.消除类与使用它的客户代码之间的耦合 门面模式常常是开发人员最亲密的朋友.它几乎是所有javascript库的核心原则 门面模式的目的是为了让开发人员用更简单的方法调用一些相对复杂或组合的方法,主要就是简化开发的复杂性,提供一个相对容易的API去调用内部的方法供外界去使用,这样程序员开发会变得轻松些,编写一次组合代码后可以反复的去使用它,有助于节省时间和精力 注意: 不要滥用门面模式,所以使用你心仪的门面之前一定要三思而定,搞不好你就会小题大做 引入概念