willson-wang / Blog

随笔
https://blog.willson-wang.com/
MIT License
70 stars 10 forks source link

underscore源码解析(二) #17

Open willson-wang opened 6 years ago

willson-wang commented 6 years ago

接着上一篇,继续分析,显然underscore0.1.0版本,还不是最优的版本,也不能去覆盖更多的使用场景,所以必然会去继续优化。所以从0.2.0开始看,但是当自己去总结的时候如果一个版本一个版本去总结分析的话,也不合理,因为有些小版本只是添加了一个方法,or修改了某个方法的兼容性,索性从0.2.0一直到1.0.0这中间的版本合到一起来分析,学下作者当时的优化思路及新增的方法是为了哪些业务场景;

总结下0.2.0版本到1.0.0版本之间,作者主要新增及优化了哪些内容,如下所示:

  1. 增加了noConflict方法,避免在underscore加载完之后,覆盖之前定义or加载的_符号;
  2. 新增了面向对象OOP的写法,通过改写了之前定义underscore的方式,即支持.each(),也支持().map(),同时所有的静态方法定义在underscore函数上
  3. 增加了对服务端的兼容即新增了适配commonJs的方法
  4. 语义化了部分方法的名称,如将all方法改成every方法,any方法改成some方法,向ECMASCRIPT标准靠拢;
  5. 语义化了原生对象上的属性or方法;
  6. 新增了部分方法如isEmpty、isArguments等方法
  7. 优化了部分方法的内部实现如each方法,keys方法
  8. 扩展了部分方法如foldl、methods等
  9. 支持链式调用
  10. 抽出了部分公共属性or方法如breaker,breakLoop、identity方法
  11. 修改了定义underscore的方法,从window. = {} 变成了自执行函数内root. = _
  12. each方法在回调函数处理上添加了第三个参数及把传入的集合也传入到了回调函数内,所以只要内部用到each方法的方法,回调函数都增加了第三个参数
  13. 所有之前用if来判断回调函数的值的写法,然后在条件判断内进行相应(单行)操作的写法,都用&操作符来进行了改写,这样可以使代码更简洁

一、基础设置

使用root变量来保存当前的全局对象,root = this; 保证浏览器端与服务端(node)端的兼容性

使用previousUnderscore保存了underscore加载完之前的全局对象上的变量,previousUnderscore = root.

使用了breaker变量来替换中断循环字符串'break',breaker = typeof StopIteration !== 'undefined' ? StopIteration : 'break'; 查了下资料StopIteration这个方法大部分浏览器不支持;

使用ArrayProto,ObjProto分别保存了数组的原型即对象原型 ArrayProto = Array.prototype, ObjProto = Object.prototype

使用变量保存了一些数组or对象原型上的方法

var slice                 = ArrayProto.slice,
      unshift               = ArrayProto.unshift,
      toString              = ObjProto.toString,
      hasOwnProperty        = ObjProto.hasOwnProperty,
      propertyIsEnumerable  = ObjProto.propertyIsEnumerable;

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;

定义成了一个函数var = function(obj) { return new wrapper(obj); };便于支持面向对象的写法

适配commonJs if (typeof exports !== 'undefined') exports. = ;

暴露underscore全局函数 root. = ; 注意0.1.0里面直接是window._ = {}来定义的哦

二、集合方法

each方法进行了优化,优化如下1.判断条件,2.去掉了each方法支持的条件,3.变成了一个单独的函数表达式而不是直接定义在underscore上面, 4.改变了遍历对象时传入回调内的参数格式,这样优化的好处了保证遍历数组or对象,回调函数参数的一致性,代码更健壮,即向ECMASCRIPT标准靠拢;

这里有一点需要提下的是,遍历为什么要放到try catch内,原因是forEach遍历是无法中断的,而要想中断forEach的话我们可以借助throw关键字,这个关键字是可以中断当前程序的执行,如果是在try catch内的话,在catch中是能够捕获到throw抛出的异常的,这样既能够中断循环又不会让整个程序抛出异常,如果不放在try catch内而是直接throw抛异常,则会直接中断整个代码的执行

0.1.0
each : function(obj, iterator, context) {
    var index = 0;
    try {
      // 判断是否有传入的数据类型是否有forEach,有则执行
      if (obj.forEach) {
        obj.forEach(iterator, context);
      } else if (obj.length) {
        // 没有forEach,然后判断是否是数组,是数组则使用for循环,然后调用回调函数,把参数传入
        for (var i=0; i<obj.length; i++) iterator.call(context, obj[i], i);
      } else if (obj.each) {
        // 是否有each方法
        obj.each(function(value) { iterator.call(context, value, index++); });
      } else {
        // 如果以上条件都不满足就使用for in 遍历,是对对象的一个扩展
        var i = 0;
        for (var key in obj) {
          var value = obj[key], pair = [key, value];
          pair.key = key;
          pair.value = value;
          iterator.call(context, pair, i++);
        }
      }
    } catch(e) {
      if (e != '__break__') throw e;
    }
    // 最后返回传入的数据
    return obj;
  }

1.0.0
var each = _.forEach = function(obj, iterator, context) {
    try {
      if (nativeForEach && obj.forEach === nativeForEach) {
        obj.forEach(iterator, context);
      } else if (_.isNumber(obj.length)) {
        for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
      } else {
        for (var key in obj) {
          if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
        }
      }
    } catch(e) {
      if (e != breaker) throw e;
    }
    return obj;
  };

inject方法改名为reduce方法,并增加了一个判断语句,及增加了一个传入回调函数的参数,整体实现不变

0.1.0
inject : function(obj, memo, iterator, context) {
    _.each(obj, function(value, index) {
      memo = iterator.call(context, memo, value, index);
    });
    return memo;
  }

1.0.0
reduce = function(obj, memo, iterator, context) {
    if (nativeReduce && obj.reduce === nativeReduce) return obj.reduce(_.bind(iterator, context), memo);
    each(obj, function(value, index, list) {
      memo = iterator.call(context, memo, value, index, list);
    });
    return memo;
  }

reduceRight(与reduce的用法一致,只是从集合后面开始进行操作)

reduceRight = function(obj, memo, iterator, context) {
    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) return obj.reduceRight(_.bind(iterator, context), memo);
    // clone一个数组的原因是,不影响到原数组
    var reversed = _.clone(_.toArray(obj)).reverse();
    return _.reduce(reversed, memo, iterator, context);
  }

select方法更名为filter,实现原理基本不变

0.1.0
select : function(obj, iterator, context) {
    // 判断是否支持filter方法
    if (obj.filter) return obj.filter(iterator, context);
    var results = [];
    _.each(obj, function(value, index) {
      if (iterator.call(context, value, index)) results.push(value);
    });
    return results;
  }

1.0.0
filter = function(obj, iterator, context) {
    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
    var results = [];
    each(obj, function(value, index, list) {
      // 因为是直接靠返回来进行判断所以可以直接写成与操作符,代码更简洁
      iterator.call(context, value, index, list) && results.push(value);
    });
    return results;
  }

all方法改成了every方法,实现原理不变,不过这里把之前的默认函数抽出去了

0.1.0
all : function(obj, iterator, context) {
    iterator = iterator || function(v){ return v; };
    if (obj.every) return obj.every(iterator, context);
    var result = true;
    _.each(obj, function(value, index) {
        // 出现false就停止遍历
      result = result && !!iterator.call(context, value, index);
      if (!result) throw '__break__';
    });
    return result;
  }

1.0.0
every = function(obj, iterator, context) {
    // 抽成了一个单独的函数,便于公用
    iterator = iterator || _.identity;
    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
    var result = true;
    each(obj, function(value, index, list) {
      // 优化写法,先判断后赋值,让代码更简洁,抽出了breakLoop方法
      if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
    });
    return result;
  }

any方法换成some方法,实现原理基本不变

0.1.0
any : function(obj, iterator, context) {
    iterator = iterator || function(v) { return v; };
    if (obj.some) return obj.some(iterator, context);
    var result = false;
    _.each(obj, function(value, index) {
      if (result = !!iterator.call(context, value, index)) throw '__break__';
    });
    return result;
  }

1.0.0
// 优化方式与every方法一致
some = function(obj, iterator, context) {
    iterator = iterator || _.identity;
    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
    var result = false;
    each(obj, function(value, index, list) {
      if (result = iterator.call(context, value, index, list)) _.breakLoop();
    });
    return result;
  }

include优化了写法

0.1.0
include : function(obj, target) {
    // 如果是数组,则用indexOf判断
    if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
    var found = false;
    // 如果是对象
    _.each(obj, function(pair) {
      if (pair.value === target) {
        found = true;
        throw '__break__';
      }
    });
    return found;
  }

1.0.0
include = function(obj, target) {
    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
    var found = false;
    each(obj, function(value) {
      // 把赋值操作移到了判断语句之前
      if (found = value === target) _.breakLoop();
    });
    return found;
  }

pluck优化了内部实现方式,将内部的each遍历,改成了map遍历,因为map方法本身就返回一个数组,从而不需要在去定义与返回数组,简化代码

0.1.0
pluck : function(obj, key) {
    var results = [];
    _.each(obj, function(value){ results.push(value[key]); });
    return results;
  }

1.0.0
pluck = function(obj, key) {
    return _.map(obj, function(value){ return value[key]; });
  }

max方法优化了内部实现

0.1.0
max : function(obj, iterator, context) {
    // 如果没有传入回调函数,且传入的集合为数组。则直接使用Math的max方法
    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
    var result;
    _.each(obj, function(value, index) {
      // 用一个对象对报错上一个值,当当前值大于上一个值的时候,重新赋值一次,最后输出result.value最大值
      var computed = iterator ? iterator.call(context, value, index) : value;
      if (result == null || computed >= result.computed) result = {value : value, computed : computed};
    });
    return result.value;
  }

1.0.0
max = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
    // 给了result一个比较的初始值,最小值,省调了result == null的判断,让代码更健壮,更合理
    var result = {computed : -Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      优化了if语句的写法
      computed >= result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  }

min方法优化了内部实现,与max方法同理

toArray优化了内部实现,增加了对自身支持toArray方法及arguments,对象的转换方式

0.1.0
toArray : function(iterable) {
    if (!iterable) return [];
    if (_.isArray(iterable)) return iterable;
    // 否则调用map方法返回一个数组
    return _.map(iterable, function(val){ return val; });
  }

1.0.0
toArray = function(iterable) {
    if (!iterable)                return [];
    // 曾加了对自身具有toArray方法的支持判断
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return iterable;
    // 增加了对arguments参数类型的转换方式
    if (_.isArguments(iterable))  return slice.call(iterable);
    // 改变了对象转换为数组的方式,因为values的实现就是return _.map(obj, _.identity)
    return _.values(iterable);
  }

三、数组方法

first方法进行了拓展,允许传入第二个参数及第三个参数,第二个参数用来获取数组前n个元素,第三个参数用来直接获取数组的第一个元素

0.1.0
first : function(array) {
    return array[0];
  }

1.0.0
first = function(array, n, guard) {
    return n && !guard ? slice.call(array, 0, n) : array[0];
  }

rest(用于获取传入index之后的数组元素)

rest = function(array, index, guard) {
    // 如果index有值or guard为true则取index,反之取1
    return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
  }

lastIndexOf(从右至左查找数组内是否有某个元素,有则返回index,没有则返回-1)实现思路如果支持lastIndexOf,则直接调用lastIndexOf,反之从arr,length开始遍历,而不是0开始遍历

lastIndexOf = function(array, item) {
    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
    var i = array.length;
    while (i--) if (array[i] === item) return i;
    return -1;
  }

range(按某个固定值,返回包含start到stop之间的值的数组)实现思路判断传入参数的个数,然后判断起始值,结束值,确定遍历的length,最后遍历

range = function(start, stop, step) {
    var a     = _.toArray(arguments);
    // 判断参数的个数
    var solo  = a.length <= 1;
    // 如果只有一个参数,start取0,反之取传入的第一个参数,
    // 如果只有一个参数,stop取0,反之取传入的第二个参数
    // step取第三个参数,如果第三个参数为空则默认给1
    var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
    // 获取遍历的次数
    var len   = Math.ceil((stop - start) / step);
    if (len <= 0) return [];
    var range = new Array(len);
    for (var i = start, idx = 0; true; i += step) {
      // 当i - stop or  stop - i的值大于0时,停止变量,返回数组,大于0则停止遍历的原因是当step>0时,i=start,此时start - stop是要<0的,所以当>=0时表示i的值已经到stop or大于stop了;当step<0时,stop是要小于start的,所以stop - i 还是<0,后面同理
      if ((step > 0 ? i - stop : stop - i) >= 0) return range;
      range[idx++] = i;
    }
  }

四、函数方法

bind优化了实现方式,这里优化只是实现更合理一些,当传入的上下文对象为空时,不是直接返回func而是传一个空给上下文对象,含糊调用的时候上下文对象就是{}对象

0.1.0
bind : function(func, context) {
    // 如果传入的执行上下文为空则直接返回
    if (!context) return func;
    var args = _.toArray(arguments).slice(2);
    return function() {
      // 合并_.bind()时传入的参数与调用_.bind()()时传入的参数进行合并
      var a = args.concat(_.toArray(arguments));
      // 调用apply方法,传入参数
      return func.apply(context, a);
    };
  }

1.0.0
bind = function(func, obj) {
    var args = _.rest(arguments, 2);
    return function() {
      return func.apply(obj || {}, args.concat(_.toArray(arguments)));
    };
  }

bindAll优化了参数处理的方式

0.1.0
bindAll : function() {
    var args = _.toArray(arguments);
    // 第一个参数为上下文
    var context = args.pop();
    _.each(args, function(methodName) {
      context[methodName] = _.bind(context[methodName], context);
    });
  }

1.0.0
bindAll = function(obj) {
    // 获取除第一个参数之外的其它参数
    var funcs = _.rest(arguments);
    // 如果funcs为0,那么获取传入对象内方法集合,兼容参数的写法
    if (funcs.length == 0) funcs = _.functions(obj);
    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
    return obj;
  }

delay、defer优化了内部获取参数的方式

delay = function(func, wait) {
    var args = _.rest(arguments, 2);
    return setTimeout(function(){ return func.apply(func, args); }, wait);
  }

defer = function(func) {
    return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
  }

compose(允许按顺序调用函数队列(从后往前调),并获取所有函数调用完之后的返回值)

compose = function() {
    var funcs = _.toArray(arguments);
    return function() {
      // 获取_.compose()()调用时的参数
      var args = _.toArray(arguments);
      for (var i=funcs.length-1; i >= 0; i--) {
        // 从后往前调用函数,处理args
        args = [funcs[i].apply(this, args)];
      }
      // 返回args,所以这个函数主要目的是按函数依次处理_.compose()()传入的参数,并返回最终的处理结果
      return args[0];
    };
  }

五、对象方法

keys优化内部实现,实现思路是先判断是否支持元素的keys事件,如果不支持则,先判断是否是数组,如果是数组则,取数组内的所有元素,并返回,如果是对象则使用for in遍历,加一层hasOwnProperty判断

0.1.0
keys : function(obj) {
    return _.pluck(obj, 'key');
  }

keys = nativeKeys || function(obj) {
    if (_.isArray(obj)) return _.range(0, obj.length);
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
    return keys;
  }

values优化内部实现,因为map返回值为函数,所以直接调用map方法即可

0.1.0
values : function(obj) {
    return _.pluck(obj, 'value');
  }

values = function(obj) {
    return _.map(obj, _.identity);
  };

functions(获取对象内的方法,并按顺序返回一个包含改对象内所有方法的数组)

functions = function(obj) {
    // 按key对应的value来进行过滤,然后调用sort来进行排序
    return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
  }

extend优化获取参数的方式

0.1.0
extend : function(destination, source) {
    for (var property in source) destination[property] = source[property];
    return destination;
  }

1.0.0
extend = function(obj) {
    // 获取除第一个参数之外的其它参数,即允许同时扩展多个对象
    each(_.rest(arguments), function(source) {
      for (var prop in source) obj[prop] = source[prop];
    });
    return obj;
  }

clone拓展了内部功能

0.1.0
clone : function(obj) {
    return _.extend({}, obj);
  }

1.0.0
// 支持克隆数组
clone = function(obj) {
    if (_.isArray(obj)) return obj.slice(0);
    return _.extend({}, obj);
  }

isEqual优化内部判断方式及拓展了判断支持的范围

0.1.0
isEqual : function(a, b) {
    // Check object identity.
    if (a === b) return true;
    // Different types?
    // 如果不是一个类型则直接返回false
    var atype = typeof(a), btype = typeof(b);
    if (atype != btype) return false;
    // Basic equality test (watch out for coercions).
    if (a == b) return true;
    // One of them implements an isEqual()?
    if (a.isEqual) return a.isEqual(b);
    // If a is not an object by this point, we can't handle it.
    if (atype !== 'object') return false;
    // Nothing else worked, deep compare the contents.
    var aKeys = _.keys(a), bKeys = _.keys(b);
    // Different object sizes?
    if (aKeys.length != bKeys.length) return false;
    // Recursive comparison of contents.
    for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
    return true;
  }

1.0.0
isEqual = function(a, b) {
    // Check object identity.
    if (a === b) return true;
    // Different types?
    var atype = typeof(a), btype = typeof(b);
    if (atype != btype) return false;
    // Basic equality test (watch out for coercions).
    if (a == b) return true;
    // One is falsy and the other truthy.
    if ((!a && b) || (a && !b)) return false;
    // One of them implements an isEqual()?
    if (a.isEqual) return a.isEqual(b);
    // Check dates' integer values.
    if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
    // Both are NaN?
    if (_.isNaN(a) && _.isNaN(b)) return true;
    // Compare regular expressions.
    if (_.isRegExp(a) && _.isRegExp(b))
      return a.source     === b.source &&
             a.global     === b.global &&
             a.ignoreCase === b.ignoreCase &&
             a.multiline  === b.multiline;
    // If a is not an object by this point, we can't handle it.
    if (atype !== 'object') return false;
    // Check for different array lengths before comparing contents.
    if (a.length && (a.length !== b.length)) return false;
    // Nothing else worked, deep compare the contents.
    var aKeys = _.keys(a), bKeys = _.keys(b);
    // Different object sizes?
    if (aKeys.length != bKeys.length) return false;
    // Recursive comparison of contents.
    for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
    return true;
  }

isEmpty(判断数组or对象是否为空)实现思路数组利用length来判断,对象遍历来判断

isEmpty = function(obj) {
    if (_.isArray(obj)) return obj.length === 0;
    for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
    return true;
  }

isArray(判断是否是数组)实现思路是先判断是否支持isArray方法,不支持则直接判断传入的数据是否有数组原型上特有的方法

isArray = nativeIsArray || function(obj) {
    return !!(obj && obj.concat && obj.unshift);
  }

isArguments(判断是否是arguments)实现思路是判断length属性是否为number类型,是否有数组原型上的concat方法,字符串上的substr方法,函数的apply方法及对象的length属性是否可枚举从而判断是原型上的属性还是对象上的属性

isArguments = function(obj) {
    // 具有length属性,排除数组,排除字符串,排除函数、排除自定义对象上的length属性
    return obj && _.isNumber(obj.length) && !obj.concat && !obj.substr && !obj.apply && !propertyIsEnumerable.call(obj, 'length');
  }

isFunction(判断是否是函数)优化了内部实现,之所以不使用typeof来直接判断是因为typeof在检查正则时,在不同的浏览器下也会检查为function,如Chrome 1-12中,所以使用constructor属性及原型上的call与apply方法来进行判断

0.1.0
isFunction : function(obj) {
    return typeof obj == 'function';
  }

1.0.0
isFunction = function(obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
  }

isString(判断是否是字符串)通过判断空字符及是否具有原型上的charCodeAt及substr方法来进行判断

isString = function(obj) {
    return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
  }

isNumber(判断是否是数字)通过判断是否等于自身,这里使用了一个+,这个+的作用是可以进行隐式转化,可以将其它类型的值转换为number类型的值,类似于parseInt但是比parseInt更强大,另外就是直接使用Object.proptotype.toString.call()方法来进行判断

isNumber = function(obj) {
    return (obj === +obj) || (toString.call(obj) === '[object Number]');
  }

isBoolean(判断是否是布尔值)通过判断true和false来进行判断

isBoolean = function(obj) {
    return obj === true || obj === false;
  }

isDate(判断是否是日期类型)通过判断是否有原型上的getTimezoneOffset 与setUTCFullYear方法来进行判断

isDate = function(obj) {
    return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
  }

isRegExp (判断是否是正则)通过判断是否有原型上的test、exec方法及是否使用了i标志,ignoreCase 属性表明正则表达式是否使用了 "i" 标志,使用了则表示true,没使用则表示false

isRegExp = function(obj) {
    return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
  }

isNaN(判断是否是NaN)

isNaN = function(obj) {
    return _.isNumber(obj) && isNaN(obj);
  }

isNull(判断是否是null)

isNull = function(obj) {
    return obj === null;
  }

六、工具方法

noConflict(避免产生冲突)实现的思路是先保存underscore加载之前的_变量值,在调用noConflict()方法的时候,在将之前保存的值赋给全局对象下的_属性,最后返回this

ko = noConflict();然后直接使用ko代替_来调用方法,其它不变

noConflict = function() {
    root._ = previousUnderscore;
    return this;
  }

identity (返回自身)抽出来的一个公共函数

identity = function(value) {
    return value;
  }

times(调用指定的函数n次)实现思路就是一个for循环

times = function (n, iterator, context) {
    for (var i = 0; i < n; i++) iterator.call(context, i);
  }

breakLoop(中断循环)抽出来的一个公共函数,breaker是基础设置那里定义的一个变量

breakLoop = function() {
    throw breaker;
  }

mixin(混合方法)实现思路就是一个简单的遍历,用于将wrapper原型上绑定上underscore上的静态方法

each(_.functions(obj), function(name){
      addToWrapper(name, _[name] = obj[name]);
    });
  }

uniqueId优化了内部实现在0.1.0中是包值赋给了_上的idCounter属性,而在1.0.0中是直接定义了idCounter这个变量,利用的是闭包的特性

0.1.0
uniqueId : function(prefix) {
    var id = this._idCounter = (this._idCounter || 0) + 1;
    return prefix ? prefix + id : id;
  }

1.0.0
var idCounter = 0;
uniqueId = function(prefix) {
    var id = idCounter++;
    return prefix ? prefix + id : id;
  }

template优化了内部实现

0.1.0
template : function(str, data) {
    var fn = new Function('obj', 
      'var p=[],print=function(){p.push.apply(p,arguments);};' +
      'with(obj){p.push(\'' +
      str
        .replace(/[\r\t\n]/g, " ") 
        .split("<%").join("\t") 
        .replace(/((^|%>)[^\t]*)'/g, "$1\r") 
        .replace(/\t=(.*?)%>/g, "',$1,'") 
        .split("\t").join("');") 
        .split("%>").join("p.push('") 
        .split("\r").join("\\'") 
    + "');}return p.join('');");
    return data ? fn(data) : fn;  
  }

1.0.0
templateSettings = {
    start       : '<%',
    end         : '%>',
    interpolate : /<%=(.+?)%>/g
  }
template = function(str, data) {
    var c  = _.templateSettings;
    var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g");
    var fn = new Function('obj',
      'var p=[],print=function(){p.push.apply(p,arguments);};' +
      'with(obj){p.push(\'' +
      str.replace(/[\r\t\n]/g, " ")
         .replace(endMatch,"\t")
         .split("'").join("\\'")
         .split("\t").join("'")
         .replace(c.interpolate, "',$1,'")
         .split(c.start).join("');")
         .split(c.end).join("p.push('")
         + "');}return p.join('');");
    return data ? fn(data) : fn;
  }

六、别名方法

  _.each     = _.forEach;
  _.foldl    = _.inject       = _.reduce;
  _.foldr    = _.reduceRight;
  _.select   = _.filter;
  _.all      = _.every;
  _.any      = _.some;
  _.head     = _.first;
  _.tail     = _.rest;
  _.methods  = _.functions;

七、OOP设置

定义wrapper函数用于支持面向对象的写法

var wrapper = function(obj) { this._wrapped = obj; };

定义实现链式调用方法

var result = function(obj, chain) {
    return chain ? _(obj).chain() : obj;
  }

定义将wrapper原型添加上underscore上的静态方法

var addToWrapper = function(name, func) {
    wrapper.prototype[name] = function() {
      var args = _.toArray(arguments);
      // 将this._wrapped压入arguments内
      unshift.call(args, this._wrapped);
      //调用_[name].apply(_,args)
      return result(func.apply(_, args), this._chain);
    };
  }

.mixin(); //将underscore上的静态方法混合到wrapper的原型上

// 将pop、push等原型上的方法扩展到wrapper的原型上来,注意这些方法只扩展到了wrapper的原型
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      method.apply(this._wrapped, arguments);
      return result(this._wrapped, this._chain);
    };
  })

each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      return result(method.apply(this._wrapped, arguments), this._chain);
    };
  })

链式调用的入口函数

wrapper.prototype.chain = function() {
    this._chain = true;
    return this;
  }

链式调用之后的取值函数

wrapper.prototype.value = function() {
    return this._wrapped;
  }

资源链接及参考链接 https://github.com/jashkenas/underscore/tree/1.0.0 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/typeof https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase