Underscore源码剖析(一)

本文同步自我得博客:http://www.joeray61.com

近来预备折腾一下backbone.js,在事前相识了backbone以后,我知道了backboneunderscore这个库有着强依靠,恰好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中一切要领的详细完成举行引见,谢谢关注 
    原文作者:JoeRay61
    原文地址: https://segmentfault.com/a/1190000000515420
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞