underscore 0.1.0版本源码阅读

前言

这篇文章是为之后的underscore现版本的源码做铺垫,先感受下最先版本

  1. 0.1.0版本足够小
  2. 这个版本已经有将近小10年的历史了
  3. 还是有一些不错的地方。

0.1.0版本源码分析

// Underscore.js
// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the terms of the MIT license.
// Portions of Underscore are inspired by or borrowed from Prototype.js, 
// Oliver Steele's Functional, And John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore/
window._ = {
  
  VERSION : '0.1.0',
  
  /*------------------------ Collection Functions: ---------------------------*/
  // 集合函数
  // The cornerstone, an each implementation.
  // Handles objects implementing forEach, each, arrays, and raw objects.

  each : function(obj, iterator, context) {
    var index = 0;
    try {
      if (obj.forEach) {
        // 有forEach优先选择forEach
        obj.forEach(iterator, context);
      } else if (obj.length) {
        // 使用自定义迭代器迭代
        for (var i=0; i= result.computed) result = {value : value, computed : computed};
      // 对初始化以及每一次判断computed大于当前值更新
    });
    return result.value;
  },
  
  // Return the minimum element (or element-based computation).
  min : function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
    var result;
    _.each(obj, function(value, index) {
      var computed = iterator ? iterator.call(context, value, index) : value;
      if (result == null || computed < result.computed) result = {value : value, computed : computed};
    });
    return result.value;
  },
  
  // Sort the object's values by a criteria produced by an iterator.
  sortBy : function(obj, iterator, context) {
    // 根据对象的一些值进行排序
    // 首先我们只要关注每个函数的目的即可
    // map一次遍历,返回多个对象数组。然后根据数组对象排序
    // 并且对象均有值为criteria表示我们迭代函数的值(就是根据什么排序)
    return _.pluck(_.map(obj, function(value, index) {
      return {
        value : value,
        criteria : iterator.call(context, value, index)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      // 判断大于小于等于
      return a < b ? -1 : a > b ? 1 : 0;
    }), 'value');
    // 外面的一层pluck是为了解开map函数的一层打包
  },
  
  // Use a comparator function to figure out at what index an object should
  // be inserted so as to maintain order. Uses binary search.
  // 这是利用二分查找吧
  // 利用二分查找找出元素应该插入到哪个位置中
  sortedIndex : function(array, obj, iterator) {
    iterator = iterator || function(val) { return val; };
    // 初始化一个迭代器
    var low = 0, high = array.length;//不严谨直接去length
    // 初始化高低位
    // 
    while (low < high) {
      var mid = (low + high) >> 1;
      // 对中位取半(important快捷方法)
      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
    }
    return low;
  },
  
  // Convert anything iterable into a real, live array.
  // 转换数组(转换一切可迭代的)
  toArray : function(iterable) {
    if (!iterable) return []; //为假值直接返回[]
    if (_.isArray(iterable)) return iterable; //判断是否为数组
    return _.map(iterable, function(val){ return val; });//如果为对象的话,利用map转成数组
  },
  
  // Return the number of elements in an object.
  // 返回对象的元素数量
  size : function(obj) {
    return _.toArray(obj).length;
  },
  
  /*-------------------------- Array Functions: ------------------------------*/
  
  // Get the first element of an array.
  // 返回数组第一个元素
  first : function(array) {
    return array[0];
  },
  
  // Get the last element of an array.
  // 返回数组最后一个元素
  last : function(array) {
    return array[array.length - 1];
  },
  
  // Trim out all falsy values from an array.
  //去除假值的数组元素
  //需要传入一个操作函数 false值在两次取反会被去掉
  compact : function(array) {
    return _.select(array, function(value){ return !!value; });
  },
  
  // Return a completely flattened version of an array.
  //多维数组返回一个一维数组
  // 数组扁平化
  // 开始展现出函数式编程的灵活性了
  flatten : function(array) {
    return _.inject(array, [], function(memo, value) {
      // 这边如果还是数组的话进行一个递归的扁平
      if (_.isArray(value)) return memo.concat(_.flatten(value));
      memo.push(value);
      return memo;
    });
  },
  
  // Return a version of the array that does not contain the specified value(s).
  // 对传入的数组进行筛选
  // 这里有一个点,我们在传参形参定义了array
  // 而在下面的地方我们可以直接使用array且slice截取arguments
  
  without : function(array) {
    var values = array.slice.call(arguments, 0);
    return _.select(array, function(value){ return !_.include(values, value); });
  },
  
  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  // 唯一的数组。有一个参数可以选择是否排序的数组,是的话会选择最快的算法
  uniq : function(array, isSorted) {
    return _.inject(array, [], function(memo, el, i) {
      if (0 == i || (isSorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
      return memo;
    });
  },
  
  // Produce an array that contains every item shared between all the 
  // passed-in arrays.
  // 筛选多个元素数组的相同值
  intersect : function(array) {
    var rest = _.toArray(arguments).slice(1);
    // 获得其余多个参数
    // 最外面肯定是一层筛选。
    // 里面做筛选的条件
    return _.select(_.uniq(array), function(item) {
      return _.all(rest, function(other) { 
        // 目的在于取交集
        // 所以我们使用外层的item 对比层的个个数组 据此我们返回同时存在多个数组中的元素
        return _.indexOf(other, item) >= 0;
      });
    });
  },
  
  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  zip : function() {
    var args = _.toArray(arguments);
    var length = _.max(_.pluck(args, 'length'));
    // 返回最大数组的长度。
    var results = new Array(length);
    // 创建一个存放点
    for (var i=0; i提取替换
    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;  
  }
  
};

总结

  • 后面的模板实现挺亮眼。
  • try catch 设计each的跳出。
  • >> 取半的快捷
  • 函数的复用。(有部分也许是不高效)
  • 整个版本时间很前。所以我们可以从中看到一些现代api的影子,也许是在现代api中看到它们的影子。
  • 源码篇幅较少,加上注释也不过400行左右。整篇阅读下来也没有很大的障碍,就是有复用性相对较高,但是对着test文件看看测试用例也就好了~~~

可以通过这些地方联系我

我的博客
我的邮箱:[email protected]

你可能感兴趣的:(javascript,underscore)