underscore 源码解析1 -- 骨架剖析
这是我们源码分析的第一章,主要是从整体的角度来剖析一个库的架构,不会拘泥于细枝末节。大体如下:
1 | (function(){ |
- 外边调用了 call ,把 this 绑定。然后用局部变量 root 来接收 this。this 根据代码使用的平台不一样也是不同的。比如, 客户端为 ‘window’, 服务端(node) 中为 ‘exports’
- 如果之前的 this 上有 _ 这个变量, 怎么防止冲突?代码里面用 previousUnderscore 接收了 root._,后面会进行处理冲突
接下来要开始构造我们自己的变量 _ ,这是最关键的一步,所有的方法都是挂载在这个变量上的。_ 其实是一个构造函数,支持无 new 调用(思考 jQuery 的无 new 调用)。一般有两种使用方法
- _.each([1,2,3], alert)
- _([1, 2, 3]).each(alert) (OOP 调用)
通常我们会使用第二种方式。我们看一下代码实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Create a safe reference to the Underscore object for use below.
// 核心函数
// `_` 其实是一个构造函数
// 支持无 new 调用(思考 jQuery 的无 new 调用)
// 将传入的参数(实际要操作的数据)赋值给 this._wrapped 属性
// OOP 调用时,_ 相当于一个构造函数
// each 等方法都在该构造函数的原型链上
// _([1, 2, 3]).each(alert)
// _([1, 2, 3]) 相当于无 new 构造了一个新的对象
// 调用了该对象的 each 方法,该方法在该对象构造函数的原型链上
var _ = function(obj) {
// 以下均针对 OOP 形式的调用
// 如果是非 OOP 形式的调用,不会进入该函数内部
// 如果 obj 已经是 `_` 函数的实例,则直接返回 obj
if (obj instanceof _)
return obj;
// 如果不是 `_` 函数的实例
// 则调用 new 运算符,返回实例化的对象
if (!(this instanceof _))
return new _(obj);
// 将 obj 赋值给 this._wrapped 属性
this._wrapped = obj;
};
_ 这个变量定义好了,接下来我们要挂载到 this 上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object.
// 将上面定义的 `_` 局部变量赋值给全局对象中的 `_` 属性
// 即客户端中 window._ = _
// 服务端(node)中 exports._ = _
// 同时在服务端向后兼容老的 require() API
// 这样暴露给全局后便可以在全局环境中使用 `_` 变量(方法)
if (typeof exports !== 'undefined') {
// node 环境
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
// 如果 exports 未定义,也就是说是在浏览器中
root._ = _;
}