资讯专栏INFORMATION COLUMN

underscore源码解析

kid143 / 883人阅读

说明
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 = {
    "&": "&",
    "<": "<",
    ">": ">",
    """: """,
    """: "'",
    "`": "`"
  };
  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    : /<%([sS]+?)%>/g,
    interpolate : /<%=([sS]+?)%>/g,
    escape      : /<%-([sS]+?)%>/g
  };
  var noMatch = /(.)^/;
  var escapes = {
    """:      """,
    "":     "",
    "
":     "r",
    "
":     "n",
    "u2028": "u2028",
    "u2029": "u2029"
  };
  var escaper = /|"|
|
|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; 
};

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107478.html

相关文章

  • Underscore源码解析(四)

    摘要:本文同步自我得博客我在这个系列的第一篇文章说过,我学是为了在学的时候少一些阻碍,从第一篇的写作时间到今天,大概也有个十几二十天,感觉拖得有点久,所以今天将会是源码解析系列的最后一篇文章,我会在这篇文章中介绍剩下的所有函数。 本文同步自我得博客:http://www.joeray61.com 我在这个系列的第一篇文章说过,我学underscore是为了在学backbone的时候少一些阻碍...

    高胜山 评论0 收藏0
  • Underscore源码解析(一)

    摘要:本文同步自我得博客最近准备折腾一下,在事先了解了之后,我知道了对这个库有着强依赖,正好之前也没使用过,于是我就想先把彻底了解一下,这样之后折腾的时候也少一点阻碍。 本文同步自我得博客:http://www.joeray61.com 最近准备折腾一下backbone.js,在事先了解了backbone之后,我知道了backbone对underscore这个库有着强依赖,正好undersc...

    neu 评论0 收藏0
  • Underscore源码解析(二)

    摘要:本文同步自我得博客最近十几天都在忙毕业论文的事,所以上一次为大家介绍完这个框架的结构或者说是这个框架的设计思路之后就一直没动静了,今天我又满血复活了,让我们继续来探索的源码奥秘吧。 本文同步自我得博客:http://www.joeray61.com 最近十几天都在忙毕业论文的事,所以上一次为大家介绍完underscore这个框架的结构(或者说是这个框架的设计思路)之后就一直没动静了,今...

    骞讳护 评论0 收藏0
  • JS基础篇-underscore源码解析

    摘要:总想找个机会夯实一下自己的基础,正好最近略有清闲,看视频读书撸代码我选择了第三者怎么感觉有点别扭,看视频的话效率不高适合入门,看书的话一本你不知道的推荐给大家,选择继续看书的话还是算了吧,毕竟读万卷书不如行万里路是吧。 总想找个机会夯实一下自己的JS基础,正好最近略有清闲,看视频?读书?撸代码?我选择了第三者(怎么感觉有点别扭),看视频的话效率不高适合入门,看书的话,一本《你不知道的J...

    anyway 评论0 收藏0
  • Underscore源码中文注释(转)

    摘要:创建一个全局对象在浏览器中表示为对象在中表示对象保存下划线变量被覆盖之前的值如果出现命名冲突或考虑到规范可通过方法恢复被占用之前的值并返回对象以便重新命名创建一个空的对象常量便于内部共享使用将内置对象的原型链缓存在局部变量方便快速调用将 // Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc....

    Guakin_Huang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<