说明下,写这个不是说明接下去不分析jquery了,只是这两天在准备面试,顺便把seajs给读了,调节下心情。把seajs的源码分析速度写完以后,就会继续jquery。。。
===================================================================================
首先是seajs的大架构:
1 /** 2 * Sea.js 2.2.1 | seajs.org/LICENSE.md 3 */ 4 (function(global, undefined) { 5 //some code 6 })(this)
采用了闭包的结构,这样就不会干扰全局变量。在页面中的正常引用时,因未指定this具体指向,所以会自动设为window对象。这样就把global=window,undefined传入闭包,方便闭包里的特定对象属性暴露到全局。
接下去这段就是说明把seajs变量暴露到全局,并附带版本号:
//如果全局中已经有seajs就直接返回 if (global.seajs) { return } var seajs = global.seajs = { // The current version of Sea.js being used version: "2.2.1" } var data = seajs.data = {}
接下去的内容主要是用于判断类型的:
function isType(type) { return function(obj) { return {}.toString.call(obj) == "[object " + type + "]" } } var isObject = isType("Object") var isString = isType("String") var isArray = Array.isArray || isType("Array") var isFunction = isType("Function") var _cid = 0 function cid() { return _cid++ }
可以看到,seajs的类型判断基本是用isType的,它会根据传入值返回一个函数,这个函数能够判断接下来需要判断的类型和传入的是否相同。其内部调用的是Object.prototype.toString原生函数。
cid用于返回特定唯一值。
接下来是seajs里的事件机制,事件机制主要是方便插件的开发,在seajs运行到特定步骤时,都会激发一些列事件,插件通过将函数插入这些事件中,做到在特定点运行特定动作,这个原理在《javascript高级程序设计》里有提到过,源码如下:
var events = data.events = {} // 绑定事件,将事件放入特定的事件名称下 seajs.on = function(name, callback) { var list = events[name] || (events[name] = []) list.push(callback) return seajs } // 根据name,callback来移除事件 // 如果name和callback都未定义的话,移除所有事件 seajs.off = function(name, callback) { // 移除所有事件 if (!(name || callback)) { events = data.events = {} return seajs } var list = events[name] if (list) { if (callback) { // 移除特定事件名称下的特定函数 for (var i = list.length - 1; i >= 0; i--) { if (list[i] === callback) { list.splice(i, 1) } } } else { // 未指定callback的话,就直接移除所有该事件名称下的事件 delete events[name] } } return seajs } // 运行事件,将所有该事件名称下的事件全部跑一遍 var emit = seajs.emit = function(name, data) { var list = events[name], fn if (list) { // 复制事件,防止对原先事件的修改 list = list.slice() // 运行每一项事件 while ((fn = list.shift())) { fn(data) } } return seajs }
接下来是seajs中关于路径处理的,将路径转化,加上alias,map之类的,源码如下:
//文件夹目录匹配 var DIRNAME_RE = /[^?#]*// // 匹配/./ var DOT_RE = //.//g // 匹配/目录名/../ var DOUBLE_DOT_RE = //[^/]+/..// // 匹配双斜杠 var DOUBLE_SLASH_RE = /([^:/])///g // 得到文件夹目录 // dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/" function dirname(path) { return path.match(DIRNAME_RE)[0] } // 路径转化 // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c" function realpath(path) { // /a/b/./c/./d ==> /a/b/c/d path = path.replace(DOT_RE, "/") // a/b/c/../../d ==> a/b/../d ==> a/d while (path.match(DOUBLE_DOT_RE)) { path = path.replace(DOUBLE_DOT_RE, "/") } // a//b/c ==> a/b/c path = path.replace(DOUBLE_SLASH_RE, "$1/") return path } // 转化id成路径 // normalize("path/to/a") ==> "path/to/a.js" // substring比slice和RegExp快 function normalize(path) { var last = path.length - 1 var lastC = path.charAt(last) // 若uri以#结尾,则直接返回去掉#后的结果 if (lastC === "#") { return path.substring(0, last) } // 在最后加.js return (path.substring(last - 2) === ".js" || path.indexOf("?") > 0 || path.substring(last - 3) === ".css" || lastC === "/") ? path : path + ".js" } // paths vars匹配 var PATHS_RE = /^([^/:]+)(/.+)$/ var VARS_RE = /{([^{]+)}/g // 取得相应id下的alias function parseAlias(id) { var alias = data.alias return alias && isString(alias[id]) ? alias[id] : id } // 根据设置的data.paths转化id function parsePaths(id) { var paths = data.paths var m if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) { id = paths[m[1]] + m[2] } return id } // 根据设置的vars 转化id function parseVars(id) { var vars = data.vars if (vars && id.indexOf("{") > -1) { id = id.replace(VARS_RE, function(m, key) { return isString(vars[key]) ? vars[key] : m }) } return id } // 根据在map中设置的规则转化uri function parseMap(uri) { var map = data.map var ret = uri if (map) { for (var i = 0, len = map.length; i < len; i++) { var rule = map[i] ret = isFunction(rule) ? (rule(uri) || uri) : uri.replace(rule[0], rule[1]) // 只运行第一条匹配的规则 if (ret !== uri) break } } return ret } // 绝对路径 根目录匹配 var ABSOLUTE_RE = /^//.|:// var ROOT_DIR_RE = /^.*?//.*?// function addBase(id, refUri) { var ret var first = id.charAt(0) // 绝对路径 if (ABSOLUTE_RE.test(id)) { ret = id } // 关于相对路径处理,判断是否有传入refUri,否则用data.cwd else if (first === ".") { ret = realpath((refUri ? dirname(refUri) : data.cwd) + id) } // 根路径 else if (first === "/") { var m = data.cwd.match(ROOT_DIR_RE) ret = m ? m[0] + id.substring(1) : id } // 其它 else { ret = data.base + id } // 若以//开头,则添加默认的protocol if (ret.indexOf("//") === 0) { ret = location.protocol + ret } return ret } // id到uri的转化函数 function id2Uri(id, refUri) { if (!id) return "" id = parseAlias(id) id = parsePaths(id) id = parseVars(id) id = normalize(id) var uri = addBase(id, refUri) uri = parseMap(uri) return uri }