1

说明
1、源码结构通览,简单注释说明
2、通过调用方法讲解核心代码逻辑

一、源码的结构

为了方便比对源码,按源码的结构顺序展示。
underscore是个轻量级的工具库,大部分代码是实现特定功能以函数的形式存在,本身会比较简单,没对方法具体说明,可直接参考underscore中文文档

(function() {
  var root = this;
  var previousUnderscore = root._;
  //原型赋值给变量,好处是方便压缩
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  //方便直接调用原型上的方法
  var
    push             = ArrayProto.push,
    slice            = ArrayProto.slice,
    toString         = ObjProto.toString,
    hasOwnProperty   = ObjProto.hasOwnProperty;
  var
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind,
    nativeCreate       = Object.create;
  var Ctor = function(){};
  //判断obj是否是_的实例,是返回实例,不是下一步;
  //判断this是否为_的实例,是把obj赋于this实例的变量_wrapped
  //不是返回一个新实例,执行这个新实例时,新实例里面的this指向该新实例。
  //把obj赋于新实例的变量_wrapped
  var _ = function(obj) {};
  //根据环境将_赋值给不同变量
  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root._ = _;
  }
  _.VERSION = '1.8.3';
  //void 0 是undefined的意思
  //context === void 0返回func;
  //根据argCount的值返回不同的函数
  var optimizeCb = function(func, context, argCount) {};
  //根据value类型返回不同函数
  var cb = function(value, context, argCount) {};
  //调用cb()
  _.iteratee = function(value, context) {};
  //返回函数func(obj){}
  //调用func时 arguments长度小于2 或 obj 为空,返回obj
  //拷贝第二个开始往后参数的属性值(undefinedOnly不为真 或 obj无此属性)给obj
  var createAssigner = function(keysFunc, undefinedOnly) {};
  //实现Object.create()
  var baseCreate = function(prototype) {};
  //返回函数func(obj){}
  //调用func时 obj不为空返回 obj[key]
  var property = function(key) {};
  //2的53次方减1
  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
  //获取对象的长度
  var getLength = property('length');
  //通过length属性 类数组判断
  var isArrayLike = function(collection) {};

  _.each = _.forEach = function(obj, iteratee, context) {};
  _.map = _.collect = function(obj, iteratee, context) {};

  //返回函数function(obj, iteratee, memo, context);
  //调用返回函数 为将obj的值以 dir正负代表左右步数 的顺序代入iteratee中(memo有值,以momo为第一个参数),返回最终的结果
  function createReduce(dir) {}

  //从左到右步进1,执行createReduce返回函数
  _.reduce = _.foldl = _.inject = createReduce(1);
  //从右到左步进1,执行createReduce返回函数
  _.reduceRight = _.foldr = createReduce(-1);
  _.find = _.detect = function(obj, predicate, context) {};
  _.filter = _.select = function(obj, predicate, context) {};
  _.reject = function(obj, predicate, context) {};
  _.every = _.all = function(obj, predicate, context) {};
  _.some = _.any = function(obj, predicate, context) {};
  _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {};
  _.invoke = function(obj, method) {};
  _.pluck = function(obj, key) {};
  _.where = function(obj, attrs) {};
  _.findWhere = function(obj, attrs) {};

  // Infinity为无穷大
  _.max = function(obj, iteratee, context) {};
  _.min = function(obj, iteratee, context) {};
  _.shuffle = function(obj) {};
  _.sample = function(obj, n, guard) {};
  _.sortBy = function(obj, iteratee, context) {};
  //内部方法,返回function(obj, iteratee, context)供groupBy、indexBy、countBy调用
  var group = function(behavior) {};

  _.groupBy = group(function(result, value, key) {});
  _.indexBy = group(function(result, value, key) {});
  _.countBy = group(function(result, value, key) {});
  _.toArray = function(obj) {};
  _.size = function(obj) {};
  _.partition = function(obj, predicate, context) {};
  _.first = _.head = _.take = function(array, n, guard) {};
  _.initial = function(array, n, guard) {};
  _.last = function(array, n, guard) {};
  _.rest = _.tail = _.drop = function(array, n, guard) {};
  _.compact = function(array) {};
  //内部方法,返回数组供flatten、union、difference、pick调用
  var flatten = function(input, shallow, strict, startIndex) {};
  _.flatten = function(array, shallow) {};
  _.without = function(array) {};
  _.uniq = _.unique = function(array, isSorted, iteratee, context) {};
  _.union = function() {};
  _.intersection = function(array) {};
  _.difference = function(array) {};
  _.zip = function() {};
  _.unzip = function(array) {};
  _.object = function(list, values) {};
  //内部方法,返回function(obj, iteratee, context)
  function createPredicateIndexFinder(dir) {}
  _.findIndex = createPredicateIndexFinder(1);
  _.findLastIndex = createPredicateIndexFinder(-1);
  _.sortedIndex = function(array, obj, iteratee, context) {};
  function createIndexFinder(dir, predicateFind, sortedIndex) {}
  _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
  _.range = function(start, stop, step) {};
  var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {};
  _.bind = function(func, context) {};
  _.partial = function(func) {};
  _.bindAll = function(obj) {};
  _.memoize = function(func, hasher) {};
  _.delay = function(func, wait) {};
  _.defer = _.partial(_.delay, _, 1);
  _.throttle = function(func, wait, options) {};
  };
  _.debounce = function(func, wait, immediate) {};
  _.wrap = function(func, wrapper) {};
  _.negate = function(predicate) {};
  _.compose = function() {};
  _.after = function(times, func) {};
  _.before = function(times, func) {};
  _.once = _.partial(_.before, 2);
  var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
  var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
                      'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];

  function collectNonEnumProps(obj, keys) {}
  _.keys = function(obj) {};
  _.allKeys = function(obj) {};
  _.values = function(obj) {};
  _.mapObject = function(obj, iteratee, context) {};
  _.pairs = function(obj) {};
  _.invert = function(obj) {};
  _.functions = _.methods = function(obj) {};
  _.extend = createAssigner(_.allKeys);
  _.extendOwn = _.assign = createAssigner(_.keys);
  _.findKey = function(obj, predicate, context) {};
  _.pick = function(object, oiteratee, context) {};
  _.omit = function(obj, iteratee, context) {};
  _.defaults = createAssigner(_.allKeys, true);
  _.create = function(prototype, props) {};
  _.clone = function(obj) {};
  _.tap = function(obj, interceptor) {};
  _.isMatch = function(object, attrs) {};
  //+a为parseInt(a);递归判断引用类型
  var eq = function(a, b, aStack, bStack) {};
  _.isEqual = function(a, b) {};
  _.isEmpty = function(obj) {};
  _.isElement = function(obj) {};
  _.isArray = nativeIsArray || function(obj) {};
  _.isObject = function(obj) {};
  //批量添加类型判断的方法
  _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
    _['is' + name] = function(obj) {
      return toString.call(obj) === '[object ' + name + ']';
    };
  });
  //arguments.callee当前正在执行函数,严格模式禁止使用
  if (!_.isArguments(arguments)) {}
  //typeof /./为object; typeof Int8Array为function
  if (typeof /./ != 'function' && typeof Int8Array != 'object') {
    _.isFunction = function(obj) {
      return typeof obj == 'function' || false;
    };
  }
  // isFinite(obj)检查obj是否是无穷大。
  _.isFinite = function(obj) {};
  _.isNaN = function(obj) {};
  _.isBoolean = function(obj) {};
  _.isNull = function(obj) {};
  _.isUndefined = function(obj) {};
  _.has = function(obj, key) {};
  //原root._指向_;root._ = previousUnderscore将root._指向页面原有的_;
  //this指向_
  _.noConflict = function() {};
  _.identity = function(value) {};
  _.constant = function(value) {};
  _.noop = function(){};
  _.property = property;
  _.propertyOf = function(obj) {};
  _.matcher = _.matches = function(attrs) { };
  _.times = function(n, iteratee, context) {};
  _.random = function(min, max) {};
  _.now = Date.now || function() {};
  var escapeMap = {
    '&': '&',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    '`': '&#x60;'
  };
  var unescapeMap = _.invert(escapeMap);
  var createEscaper = function(map) {};
  _.escape = createEscaper(escapeMap);
  _.unescape = createEscaper(unescapeMap);
  _.result = function(object, property, fallback) {};
  var idCounter = 0;
  _.uniqueId = function(prefix) {};
  //模板相关
  _.templateSettings = {
    evaluate    : /<%([\s\S]+?)%>/g,
    interpolate : /<%=([\s\S]+?)%>/g,
    escape      : /<%-([\s\S]+?)%>/g
  };
  var noMatch = /(.)^/;
  var escapes = {
    "'":      "'",
    '\\':     '\\',
    '\r':     'r',
    '\n':     'n',
    '\u2028': 'u2028',
    '\u2029': 'u2029'
  };
  var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
  var escapeChar = function(match) {
    return '\\' + escapes[match];
  };
  _.template = function(text, settings, oldSettings) {};

  //链式调用
  _.chain = function(obj) {};
  // 中间函数,是否链式调用
  var result = function(instance, obj) {
    return instance._chain ? _(obj).chain() : obj;
  };
  // 将obj上面的函数表达式全部挂载到_的原型上
  _.mixin = function(obj) {};
  //将_上面的函数表达式全部挂载到_的原型上
  _.mixin(_);

  // 将数组方法挂载到_的原型上(原数组改变)
  _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    _.prototype[name] = function() {
      var obj = this._wrapped;
      method.apply(obj, arguments);
      if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
      return result(this, obj);
    };
  });

  //将数组方法挂载到_的原型上(原数组不变)
  _.each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    _.prototype[name] = function() {
      return result(this, method.apply(this._wrapped, arguments));
    };
  });
  _.prototype.value = function() {};
  _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
  _.prototype.toString = function() {};
  if (typeof define === 'function' && define.amd) {
    define('underscore', [], function() {
      return _;
    });
  }
}.call(this));

二、两种调用方法

1、最常用的调用方式_.method(arguments)
示例1:

var arr = [1, 2, 3];
// 最常用,类函数式编程
var test1=_.map(arr, function(num) {
    return num;
});

调用流程主要代码:

var root = this;
//_函数表达式声明
var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };
//将_函数赋值给调用的exports._或 root._(根据环境)
if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root._ = _;
  }
//声明不同的方法作为_的属性值
  _.map = _.collect = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length,
        results = Array(length);
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
  };

通过以上的代码,可直接调用_.map函数。此方法比较简单易懂

2、OOP风格,类jquery调用_(arguments1).method(arguments2)
示例2:

var arr = [1, 2, 3];
// OOP风格,类jquery调用
var test2=_(arr).map(function(num) {
    return num* 3;
});

示例3:链式调用

var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];
var test3 = _.chain(stooges)
    .sortBy(function(stooge){ return stooge.age; })
    .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
    .first()
    .value();
console.log("test3");
console.log(test3);

调用流程主要代码:

var _ = function(obj) {
    //判断obj是否是_的实例,是返回实例,不是下一步;
    if (obj instanceof _) return obj;
    //判断this是否为_的实例,是把obj赋于this实例的变量_wrapped
     //不是返回一个新实例,执行这个新实例时,新实例里面的this指向该新实例。
    if (!(this instanceof _)) return new _(obj);
    //把obj赋于新实例的变量_wrapped
    this._wrapped = obj;
  };


_.chain = function(obj) {
    //判断obj是否是_的实例,是返回该实例,不是生成一个新实例;将实例赋值给instance 
    var instance = _(obj);
    //链式调用标记
    instance._chain = true;
    //返回实例
    return instance;
  };

var result = function(instance, obj) {
    //是否继续链式调用
    return instance._chain ? _(obj).chain() : obj;
  };

_.mixin = function(obj) {
    //_.functions(obj)返回obj上值类型为function的key的集合
    _.each(_.functions(obj), function(name) {
      var func = _[name] = obj[name];
      _.prototype[name] = function() {
        //如示例2,this._wrapped为arr
        var args = [this._wrapped];
         //如示例2,args变成[arr,function(num) {return num* 3;}]
        push.apply(args, arguments);
        //如示例2,返回_.map.apply(_,[arr,function(num) {return num* 3;}])
        return result(this, func.apply(_, args));
      };
    });
  };

//将_传入_.mixin
_.mixin(_);

//获取链式调用的结果
_.prototype.value = function() {
   return this._wrapped; 
};

kalakalaxyz
123 声望6 粉丝