完整代码
(function(global) {
if(global.seajs) {
return;
}
var seajs = global.seajs = {};
var data = seajs.data = {};
data.paths = {};
data.vars = {};
var DIRNAME_RE = /.*\//;
function dirname(path) {
return path.match(DIRNAME_RE)[0];
}
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 scripts = document.scripts;
var loaderScript = scripts[scripts.length - 1];
var seajsDir = dirname(loaderScript.src); //sea.js路径
var supportOnload = "onload" in loaderScript;
var cwd = dirname(location.href); //当前工作目录
var DOT_RE = /\/\.\//g;
var MULTI_SLASH_RE = /([^:/])\/+\//g;
var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\//;
function realpath(path) {
// /a/b/./c/./d ==> /a/b/c/d
path = path.replace(DOT_RE, "/");
// a///b/////c ==> a/b/c
path = path.replace(MULTI_SLASH_RE, "$1/");
// a/b/c/../../d ==> a/b/../d ==> a/d
while(path.match(DOUBLE_DOT_RE)) {
path = path.replace(DOUBLE_DOT_RE, "/");
}
return path;
}
var PATHS_RE = /^([^/:]+)(\/.+)$/;
var VARS_RE = /{([^{]+)}/g;
function parsePaths(uri) {
var m, paths = data.paths;
if(paths && (m = uri.match(PATHS_RE)) && isString(paths[m[1]])) {
uri = paths[m[1]] + m[2];
}
return uri;
}
function parseVars(uri) {
var vars = data.vars;
if(vars && uri.indexOf("{") > -1) {
uri = uri.replace(VARS_RE, function(m, key) {
return isString(vars[key]) ? vars[key] : m;
});
}
return uri;
}
// normalize("path/to/a") ==> "path/to/a.js"
function normalize(path) {
var last = path.length - 1;
return path.substring(last - 2) === ".js" ? path : path + ".js";
}
var HOST_RE = /^\/\/.|^[https?|file]:\/\/./;
var ROOT_DIR_RE = /^.*?\/\/.*?\//;
function addBase(uri, refUri) {
var ret, first = uri.charCodeAt(0);
// Host
if(HOST_RE.test(uri)) {
ret = uri.indexOf("//") === 0 ? location.protocol + uri : uri;
}
// Absolute
else if(first === 47 /* "/" */ ) {
var m = cwd.match(ROOT_DIR_RE);
ret = m[0] + uri.substring(1);
}
// Relative
else if(first === 46 /* "." */ ) {
ret = (refUri ? dirname(refUri) : cwd) + uri;
}
// default
else {
ret = seajsDir + uri;
}
return realpath(ret);
}
var head = document.getElementsByTagName("head")[0] || document.documentElement;
var currentlyAddingScript = null;
function request(url) {
var node = document.createElement("script");
addOnload(node, url);
node.async = true;
node.src = url;
currentlyAddingScript = node;
head.appendChild(node);
currentlyAddingScript = null;
}
function addOnload(node, url) {
if(supportOnload) {
node.onload = onload;
} else {
node.onreadystatechange = function() {
if(/loaded|complete/.test(node.readyState)) {
onload();
}
}
}
function onload() {
node.onload = node.onreadystatechange = null;
var mod = cachedMods[node.src];
if(supportOnload) {
mod.dependencies = anonymousMeta.deps;
mod.callback = anonymousMeta.callback;
}
mod.load();
head.removeChild(node);
node = null;
anonymousMeta = null;
mod.callback && mod.callback();
}
}
function getCurrentScript() {
if(currentlyAddingScript) {
return currentlyAddingScript;
}
var i, script, scripts = head.getElementsByTagName("script");
for(i = scripts.length - 1; i >= 0; i--) {
script = scripts[i];
if(script.readyState === "interactive") {
return script;
}
}
}
function inArray(arr, obj) {
var i = arr.length;
while(i--) {
if(arr[i] === obj) {
return true;
}
}
return false;
}
var cachExps = seajs.exports = {};
var cachedMods = seajs.cachedMods = {};
function Module() {
this.dependencies = [];
this.deps = [];
this.entry = [];
}
Module.resolve = function(uri, refUri) {
uri = parsePaths(uri);
uri = parseVars(uri);
uri = normalize(uri);
uri = addBase(uri, refUri);
return uri;
}
// Resolve module.dependencies
Module.prototype.resolve = function() {
var mod = this;
var i, deps = mod.dependencies;
for(i = 0, len = deps.length; i < len; i++) {
mod.deps[i] = Module.resolve(deps[i], mod.uri);
}
}
Module.prototype.load = function() {
var i, m, mod = this;
mod.resolve();
for(i = 0; i < mod.deps.length; i++) {
if(!cachedMods[mod.deps[i]]) {
m = cachedMods[mod.deps[i]] = new Module();
m.uri = mod.deps[i];
m.fetch();
}
}
}
// Fetch a module
Module.prototype.fetch = function() {
var mod = this;
request(mod.uri);
}
Module.prototype.call = function(call) {
var mod = this;
if(0 === mod.deps.length) {
call();
} else {
mod.n = 0;
var i, entry;
for(i = 0; i < mod.deps.length; i++) {
if(!cachExps[mod.deps[i]]) {
entry = cachedMods[mod.deps[i]].entry;
mod.n++;
if(!inArray(entry, mod)) {
entry.push(mod);
}
}
}
if(0 === mod.n) {
call();
}
}
}
Module.prototype.cutExp = function() {
var mod = this;
var i, exports = [];
for(i = 0; i < mod.deps.length; i++) {
exports.push(cachExps[mod.deps[i]]);
}
return exports;
}
Module.use = function(deps, callback) {
var mod = new Module();
deps = deps ? deps : [];
mod.dependencies = isArray(deps) ? deps : [deps];
mod.callback = function() {
function call() {
var exports = mod.cutExp();
if(isFunction(callback)) {
callback.apply(global, exports);
}
mod = null;
}
mod.call(call);
}
mod.load();
mod.callback();
}
var anonymousMeta = null;
Module.define = function(deps, factory) {
var argsLen = arguments.length;
if(argsLen === 1) {
factory = deps;
deps = [];
}
deps = isArray(deps) ? deps : [deps];
var callback = function() {
var mod = this;
function call() {
var exports = mod.cutExp();
var m, i, exp = (isFunction(factory) ? factory.apply({}, exports) : factory) || true;
cachExps[mod.uri] = exp;
for(i = 0; i < mod.entry.length; i++) {
m = mod.entry[i];
m.n--;
m.callback();
}
delete cachedMods[mod.uri];
mod = null;
}
mod.call(call);
}
if(supportOnload) {
// Save information for the script onload event
anonymousMeta = {
deps: deps,
callback: callback
};
} else {
var node = getCurrentScript();
var mod = cachedMods[node.src];
mod.dependencies = deps;
mod.callback = callback;
}
}
// Public API
seajs.use = function(deps, callback) {
Module.use(deps, callback);
return seajs;
}
global.define = Module.define;
seajs.config = function(configData) {
var curr, prev, key, k;
for(key in configData) {
curr = configData[key];
prev = data[key];
// Merge object config such as paths, vars
if(prev && isObject(prev)) {
for(k in curr) {
prev[k] = curr[k];
}
}
}
return seajs;
}
})(this);
使用方法很简单,类似seajs和requirejs,略微不同
define:
define(['a','b',...],function(a,b,...){
a.a();
b.b();
return obj;
});
//或者
define('a',function(a){
a.a();
return obj;
});
//第一个参数为依赖,第二个是工厂函数,返回带有方法的对象,用来自己开发js库。
seajs.use:
seajs.use(['a','b',...],function(a,b,...){
a.a();
b.b();
});
//或者
seajs.use('a',function(a){
a.a();
});