Underscore.js源码分析(一)
Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。弥补了 jQuery 没有实现的功能,同时Underscore又足够简单,以1.8.3为例,整个库加注释也只有1627行,非常适合新手阅读。
我们现在读的源码版本是1.8.3,可以参考文档来更好的理解源码。
整个库的开始先用一个自执行函数封装一个作用域,然后在全局对象上挂载”_”来访问整个库提供的方法。
14-16 行可以看到如下代码
var root = typeof self == 'object' && self.self ===self
&& self || typeof global == 'object' && global.global === global && global || this;
在库的开始便使用self这个变量,但是self并没有在之前定义,这个self到底是什么呢?
其实无非两种可能,要么是全局对象,要么是全局变量,但是不用声明直接定义全局变量显的很不严谨,于是查了一下资料,发现self返回的是当前窗口的只读引用,相当于window的属性,并且在一些不具有窗口的上下文环境中,比如Web Workers中也可以使用,所以self更加通用,而self.self
相当于window.self
,同理global
是在Node.js的全局对象,所以这段代码的意思非常明确,就是判断当前库所处的环境,并且把全局对象设置为root。
在19行可以看到如下代码
var previousUnderscore = root._;
这段代码是在underscore的” _ “在挂载到全局对象之前先保存”_”里面的内容,避免被覆盖,非常细致。
在22-35行可以看到如下代码
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
这段代码也很好理解,将一些常用的方法赋值给变量,同时将一些ES5原生支持的方法赋值,在以后的使用中如果浏览器支持则使用原生方法,这样性能更好。
41-45 可以看到如下代码
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
这段代码创建underscore的”_”对象,有点像单例模式,重复调用只创建一次。
52-59 可以看到如下代码
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
这段代码用来判断不同环境支持的模块加载机制,如果是node的环境则用modele.exports
导出,否则则直接挂载在全局对象,if (typeof exports != 'undefined' && !exports.nodeType)
这段判断很有意思,undefined
有两种可能,browser和node两种,然后在更具broswer中的nodeType排除在broswer中的情况。
这段代码和 14-16 行都有判断环境的作用,只是侧重点不同,是否可以整合到一起?(欢迎讨论)