說明下,寫這個不是說明接下去不分析jquery了,只是這兩天在準備面試,順便把seajs給讀了,調節下心情。把seajs的源碼分析速度寫完之後,就會繼續jquery。。。javascript
===================================================================================css
首先是seajs的大架構:java
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傳入閉包,方便閉包裏的特定對象屬性暴露到全局。jquery
接下去這段就是說明把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之類的,源碼以下:this
//文件夾目錄匹配 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 }