本文同步自我得博客:http://www.joeray61.com
近来预备折腾一下backbone.js,在事前相识了backbone以后,我知道了backbone对underscore这个库有着强依靠,恰好underscore之前也没运用过,因而我就想先把underscore完全相识一下,如许以后折腾backbone的时刻也少一点障碍。
*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的要领
将Array.prototype相干要领增加到wrapper原型, 建立的_对象就具有了Array.prototype中的要领
new _()时现实建立并返回了一个wrapper()对象, 并将原始数组存储到_wrapped变量, 并将原始值作为第一个参数挪用对应要领
以后我会对underscore中一切要领的详细完成举行引见,谢谢关注