本文同步自我得博客:http://www.joeray61.comjavascript
最近准备折腾一下backbone.js,在事先了解了backbone以后,我知道了backbone对underscore这个库有着强依赖,正好underscore以前也没使用过,因而我就想先把underscore完全了解一下,这样以后折腾backbone的时候也少一点阻碍。java
*underscore*是一个很实用且小巧的框架,提供了不少咱们在编程时须要的基本功能函数,并且他没有扩展*javascript*的原生对象,主要涉及对*Object*、*Array*、*Function*的操做。 我曾经问个人朋友[@小胡子哥][1] 怎么学习一个框架?他给个人回答是:“直接看源码。”如今想一想深感赞成,由于研究源码是最直接的学习途径,能够深刻地了解这个框架的思想和精髓,同时也能学习框架做者的编程技巧,提高本身的coding水平。 好了,题外话就说到这里,下面我们进入正题。
我此次看的underscore版本是1.3.3,整个文件也就1000多行,我把代码简化了一下,并加入了相关的注释:编程
// underscore的代码包裹在一个匿名自执行函数中 (function() { // 建立一个全局对象, 在浏览器中表示为window对象, 在Node.js中表示global对象 var root = this; // 保存"_"(下划线变量)被覆盖以前的值 // 若是出现命名冲突或考虑到规范, 可经过_.noConflict()方法恢复"_"被Underscore占用以前的值, 并返回Underscore对象以便从新命名 var previousUnderscore = root._; // 建立一个空的对象常量, 便于内部共享使用 var breaker = {}; // 将内置对象的原型链缓存在局部变量 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // 将内置对象原型中的经常使用方法缓存在局部变量 var slice = ArrayProto.slice, unshift = ArrayProto.unshift, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // 这里定义了一些JavaScript 1.6提供的新方法 // 若是宿主环境中支持这些方法则优先调用, 若是宿主环境中没有提供, 则会由Underscore实现 var nativeForEach = ArrayProto.forEach, nativeMap = ArrayProto.map, nativeReduce = ArrayProto.reduce, nativeReduceRight = ArrayProto.reduceRight, nativeFilter = ArrayProto.filter, nativeEvery = ArrayProto.every, nativeSome = ArrayProto.some, nativeIndexOf = ArrayProto.indexOf, nativeLastIndexOf = ArrayProto.lastIndexOf, nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // 建立对象式的调用方式, 将返回一个Underscore包装器, 包装器对象的原型中包含Underscore全部方法(相似与将DOM对象包装为一个jQuery对象) var _ = function(obj) { // 全部Underscore对象在内部均经过wrapper对象进行构造 return new wrapper(obj); }; // 针对不一样的宿主环境, 将Undersocre的命名变量存放到不一样的对象中 if( typeof exports !== 'undefined') {// Node.js环境 if( typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else {// 浏览器环境中Underscore的命名变量被挂在window对象中 root['_'] = _; } // 版本声明 _.VERSION = '1.3.3'; //在_对象上定义各类方法 . . . . . . // underscore对象的包装函数 var wrapper = function(obj) { // 原始数据存放在包装对象的_wrapped属性中 this._wrapped = obj; }; // 将Underscore的原型对象指向wrapper的原型, 所以经过像wrapper原型中添加方法, Underscore对象也会具有一样的方法 _.prototype = wrapper.prototype; // 返回一个对象, 若是当前Underscore调用了chain()方法(即_chain属性为true), 则返回一个被包装的Underscore对象, 不然返回对象自己 // result函数用于在构造方法链时返回Underscore的包装对象 var result = function(obj, chain) { return chain ? _(obj).chain() : obj; }; // 将一个自定义方法添加到Underscore对象中(实际是添加到wrapper的原型中, 而Underscore对象的原型指向了wrapper的原型) var addToWrapper = function(name, func) { // 向wrapper原型中添加一个name函数, 该函数调用func函数, 并支持了方法链的处理 wrapper.prototype[name] = function() { // 获取func函数的参数, 并将当前的原始数据添加到第一个参数 var args = slice.call(arguments); unshift.call(args, this._wrapped); // 执行函数并返回结果, 并经过result函数对方法链进行封装, 若是当前调用了chain()方法, 则返回封装后的Underscore对象, 不然返回对象自己 return result(func.apply(_, args), this._chain); }; }; // 将内部定义的_(即Underscore方法集合对象)中的方法复制到wrapper的原型链中(即Underscore的原型链中) // 这是为了在构造对象式调用的Underscore对象时, 这些对象也会具备内部定义的Underscore方法 _.mixin(_); // 将Array.prototype中的相关方法添加到Underscore对象中, 所以在封装后的Underscore对象中也能够直接调用Array.prototype中的方法 // 如: _([]).push() each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { // 获取Array.prototype中对应方法的引用 var method = ArrayProto[name]; // 将该方法添加到Underscore对象中(实际是添加到wrapper的原型对象, 所以在建立Underscore对象时同时具有了该方法) wrapper.prototype[name] = function() { // _wrapped变量中存储Underscore对象的原始值 var wrapped = this._wrapped; // 调用Array对应的方法并返回结果 method.apply(wrapped, arguments); var length = wrapped.length; if((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0]; // 即便是对于Array中的方法, Underscore一样支持方法链操做 return result(wrapped, this._chain); }; }); // 做用同于上一段代码, 将数组中的一些方法添加到Underscore对象, 并支持了方法链操做 // 区别在于上一段代码所添加的函数, 均返回Array对象自己(也多是封装后的Array), concat, join, slice方法将返回一个新的Array对象(也多是封装后的Array) each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; wrapper.prototype[name] = function() { return result(method.apply(this._wrapped, arguments), this._chain); }; }); // 对Underscore对象进行链式操做的声明方法 wrapper.prototype.chain = function() { // this._chain用来标示当前对象是否使用链式操做 // 对于支持方法链操做的数据, 通常在具体方法中会返回一个Underscore对象, 并将原始值存放在_wrapped属性中, 也能够经过value()方法获取原始值 this._chain = true; return this; }; // 返回被封装的Underscore对象的原始值(存放在_wrapped属性中) wrapper.prototype.value = function() { return this._wrapped; }; }).call(this);
underscore这个库的结构(或者说思路)大体是这样的:数组
建立一个包装器, 将一些原始数据进行包装,全部的*undersocre*对象, 内部均经过*wrapper*函数进行构造和封装 *underscore*与*wrapper*的内部关系是:
内部定义变量_, 将underscore相关的方法添加到_, 这样就能够支持函数式的调用, 如_.bind()浏览器
内部定义wrapper类, 将_的原型对象指向wrapper类的原型缓存
将underscore相关的方法添加到wrapper原型, 建立的_对象就具有了underscore的方法app
将Array.prototype相关方法添加到wrapper原型, 建立的_对象就具有了Array.prototype中的方法框架
new _()时实际建立并返回了一个wrapper()对象, 并将原始数组存储到_wrapped变量, 并将原始值做为第一个参数调用对应方法函数
以后我会对underscore中全部方法的具体实现进行介绍,感谢关注