自己边读变加了一些注释,理解了一下seajs3.0.0工作的流程。正则没有一个个去理解,插件模块也没看, 以后有时间了可以补充完整~

事件系统中事件队列的获取&定义方法

var list = events[name] || (events[name] = [])

以前自己写都是

if(!events[name]){
events[name]=[];
}
var list=events[name];

加载模块文件的方法

webworker环境下加载模块文件

获取seajs的加载路径:

var stack;
try {
var up = new Error();
throw up;
} catch (e) {
// IE won't set Error.stack until thrown
stack = e.stack.split('\n');
}
//  at Error (native) <- Here's your problem
// at http://localhost:8000/_site/dist/sea.js:2:4334 <- What we want
// at http://localhost:8000/_site/dist/sea.js:2:8386
// at http://localhost:8000/_site/tests/specs/web-worker/worker.js:3:1

根据url加载模块,使用的是webworker全局环境下的importScripts方法

function requestFromWebWorker(url, callback, charset) {
// Load with importScripts
var error;
try {
importScripts(url);
} catch (e) {
error = e;
}
callback(error);
}
// For Developers
seajs.request = requestFromWebWorker;

浏览器js线程中异步加载模块文件

  // 通过script标签加载脚本
function request(url, callback, charset) {
var node = doc.createElement("script") if (charset) {
var cs = isFunction(charset) ? charset(url) : charset
if (cs) {
node.charset = cs
}
} addOnload(node, callback, url) node.async = true
node.src = url // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
// the end of the insert execution, so use `currentlyAddingScript` to
// hold current node, for deriving url in `define` call
currentlyAddingScript = node // ref: #185 & http://dev.jquery.com/ticket/2709
baseElement ?
head.insertBefore(node, baseElement) :
head.appendChild(node) currentlyAddingScript = null
}
//添加加载完成之后的处理,包括报错、清除对象、移除dom等操作
function addOnload(node, callback, url) {
var supportOnload = "onload" in node if (supportOnload) {
node.onload = onload
node.onerror = function() {
emit("error", { uri: url, node: node })
onload(true)
}
}
else {
node.onreadystatechange = function() {
if (/loaded|complete/.test(node.readyState)) {
onload()
}
}
} function onload(error) {
// Ensure only run once and handle memory leak in IE
node.onload = node.onerror = node.onreadystatechange = null // Remove the script to reduce memory leak
// 添加了又去除掉
if (!data.debug) {
head.removeChild(node)
} // Dereference the node
node = null callback(error)
}
} // For Developers
seajs.request = request

核心模块类 Module

Module类定义

//定义module类
function Module(uri, deps) {
//模块路径
this.uri = uri
//这是一个字符串数组,内容是该模块依赖模块的id列表
this.dependencies = deps || []
//这是该模块所依赖的模块的一个map key为id,value为Module对象
this.deps = {} // Ref the dependence modules
//模块状态
this.status = 0
//用来辅助load的一个数组,load完成后会删掉
this._entry = []
}

模块缓存

var cachedMods = seajs.cache = {}

原型方法列表

// 获取模块中依赖模块的uri数组
Module.prototype.resolve 
// 一个辅助模块load过程的函数
Module.prototype.pass
//加载并执行模块
Module.prototype.load
//模块加载完成后触发
Module.prototype.onload
//模块404时候触发
Module.prototype.error
//执行模块
Module.prototype.exec
//用来获取模块文件,每调用一次给一个缓存对象设置一个key value,value是一个函数,调用即开始加载文件

函数方法列表

//通过id获取文件uri
Module.resolve = function(id, refUri)
//用来定义一个模块
Module.define= function (id, deps, factory)
//将id、依赖、 factory 、模块加载状态设置给缓存中的模块
Module.save= function(uri, meta)
////根据uri,从缓存中获取模块,或者以接收的uri和deps参数创建一个模块返回,并加入缓存
Module.get=function(uri, deps)
//用来加载并运行模块
Module.use = function(ids, callback, uri)

暴露出来的API

 // Public API

 seajs.use = function(ids, callback) {
//use 实际上创建了一个id为data.cwd + "_use_" + cid()的模块,这个模块的依赖模块是待启动的模块
Module.use(ids, callback, data.cwd + "_use_" + cid())
return seajs
} Module.define.cmd = {}
global.define = Module.define // For Developers seajs.Module = Module
data.fetchedList = fetchedList
data.cid = cid seajs.require = function(id) {
var mod = Module.get(Module.resolve(id))
if (mod.status < STATUS.EXECUTING) {
mod.onload()
mod.exec()
}
return mod.exports
} /**
* config.js - The configuration for the loader
*/ // The root path to use for id2uri parsing
data.base = loaderDir // The loader directory
data.dir = loaderDir // The loader's full path
data.loader = loaderPath // The current working directory
data.cwd = cwd // The charset for requesting files
data.charset = "utf-8" // data.alias - An object containing shorthands of module id
// data.paths - An object containing path shorthands in module id
// data.vars - The {xxx} variables in module id
// data.map - An array containing rules to map module uri
// data.debug - Debug mode. The default value is false seajs.config = function(configData) { for (var key in configData) {
var curr = configData[key]
var prev = data[key] // Merge object config such as alias, vars
if (prev && isObject(prev)) {
for (var k in curr) {
prev[k] = curr[k]
}
}
else {
// Concat array config such as map
if (isArray(prev)) {
curr = prev.concat(curr)
}
// Make sure that `data.base` is an absolute path
else if (key === "base") {
// Make sure end with "/"
if (curr.slice(-1) !== "/") {
curr += "/"
}
curr = addBase(curr)
} // Set config
data[key] = curr
}
} emit("config", configData)
return seajs
}

常用API和特性的理解

seajs.use原理

seajs.use ("index.js")=Module.use(["index.js"],undefined,"http://xxxx.com/xxx/xxx/_use_i")
① 通过Module.get创建模块mod,
② 通过Module.prototype.load加载mod模块,为mode定义history、remain、callback属性,设置_entry
 
Module.prototype.load中:
①通过Module.prototype.resolve函数获取模块的依赖模块uri数组。
Module.prototype.resolve会遍历mod对象的dependencies中的模块id,挨个通过Module.resolve函数获取其uri,最后返回数组
② 遍历上一步获取的uri,通过Module.get获取模块,给mod的deps属性设置值,key为依赖模块id,value为依赖模块对象
④ 通过Module.prototype.fetch 获取所有依赖模块的模块文件
⑤ 对子模块重复以上过程
⑥mod模块加载完成后,执行onload回调,在onload中执行Module.use的回调callback,在callback中执行Module.prototype.exec来运行该模块

require、exports、module的注入&懒执行

Module.use的回调callback中会运行模块的Module.prototype.exec ,返回module的exports对象。
这个过程中,若factory是function,则调用factory,参数传入require,mod.exports,mod。
require:
  function require(id) {
var m = mod.deps[id] || Module.get(require.resolve(id))
if (m.status == STATUS.ERROR) {
throw new Error('module was broken: ' + m.uri);
}
return m.exec()
}
注入的过程:
// Exec factory
var factory = mod.factory
//执行factory的代码,这个时候才执行,懒执行
var exports = isFunction(factory) ?
factory(require, mod.exports = {}, mod) :
factory
可以看到,require的作用是从缓存中获取模块,然后exec。也就是说,只有require函数被执行的时候,模块代码才会被执行。这就是seajs的as lazy as posible ,懒执行。

global.define

define函数的作用是从参数中获取 id 、dependences 、  factory,然后从模块缓存中获取模块对象,或者创建新模块加入缓存,将id、dependences、factory设置给模块。
贴上关键代码:
Module.define = function (id, deps, factory) {
var argsLen = arguments.length
// define(factory)
if (argsLen === 1) {
factory = id
id = undefined
}
else if (argsLen === 2) {
factory = deps
// define(deps, factory)
if (isArray(id)) {
deps = id
id = undefined
}
// define(id, factory)
else {
deps = undefined
}
}
// 从function字符串中获取依赖模块id
if (!isArray(deps) && isFunction(factory)) {
deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString())
}
var meta = {
id: id,
uri: Module.resolve(id),
deps: deps,
factory: factory
}
meta.uri ? Module.save(meta.uri, meta) :
// Save information for "saving" work in the script onload event
anonymousMeta = meta
}

(为了方便理解,源码中一些辅助代码被我剪切掉了)

 最后附上我写过注释的源码

seajs3.0.0源码分析记录的更多相关文章

  1. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  2. Spark2.1.0之源码分析——事件总线

    阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析——事件总线>.<Spark2.1.0事件总线分析——ListenerBus的继承体系>及<Spark2. ...

  3. jQuery 2.0.3 源码分析 事件体系结构

    那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...

  4. jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

    Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...

  5. jQuery 2.0.3 源码分析 Deferred概念

    JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...

  6. jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

    事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...

  7. jQuery 2.0.3 源码分析 Deferrred概念

    转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...

  8. jQuery 2.0.3 源码分析Sizzle引擎 - 词法解析

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工作原理略有差别,但也有一定规则. 简 ...

  9. jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + div.aaron input[type="checkb ...

随机推荐

  1. .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]

    构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...

  2. 调用AJAX做登陆和注册

    先建立一个页面来检测一下我们建立的用户名能不能用,看一下有没有已经存在的用户名吗 可以通过ajax提示一下 $("#uid").blur(function(){ //取用户名 va ...

  3. 关于面试题 Array.indexof() 方法的实现及思考

    这是我在面试大公司时碰到的一个笔试题,当时自己云里雾里的胡写了一番,回头也曾思考过,最终没实现也就不了了之了. 昨天看到有网友说面试中也碰到过这个问题,我就重新思考了这个问题的实现方法. 对于想进大公 ...

  4. [C#] C# 知识回顾 - 异常介绍

    异常介绍 我们平时在写程序时,无意中(或技术不够),而导致程序运行时出现意外(或异常),对于这个问题, C# 有专门的异常处理程序. 异常处理所涉及到的关键字有 try.catch 和 finally ...

  5. 算法与数据结构(十五) 归并排序(Swift 3.0版)

    上篇博客我们主要聊了堆排序的相关内容,本篇博客,我们就来聊一下归并排序的相关内容.归并排序主要用了分治法的思想,在归并排序中,将我们需要排序的数组进行拆分,将其拆分的足够小.当拆分的数组中只有一个元素 ...

  6. Chrome V8引擎系列随笔 (1):Math.Random()函数概览

    先让大家来看一幅图,这幅图是V8引擎4.7版本和4.9版本Math.Random()函数的值的分布图,我可以这么理解 .从下图中,也许你会认为这是个二维码?其实这幅图告诉我们一个道理,第二张图的点的分 ...

  7. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  8. Div Vertical Menu ver5

    这个小功能,如果是算此次,已经是第5次修改了.可以从这里看到前4次:V1, http://www.cnblogs.com/insus/archive/2011/10/17/2215637.html V ...

  9. css样式之border-image

    border-image-source 属性设置边框的图片的路径[none | <image>] div { border: 20px solid #000; border-image-s ...

  10. BPM配置故事之案例9-根据表单数据调整审批线路2

    老李:好久不见啊,小明. 小明:-- 老李:不少部门有物资着急使用,现在的审批流程太慢了,申请时增加一个是否加急的选项吧.如果选加急,金额1000以下的直接到我这里,我审批完就通过,超过1000的直接 ...