underscore-1.8.3-analysis.js

Posted Tomato

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了underscore-1.8.3-analysis.js相关的知识,希望对你有一定的参考价值。

   1 //     Underscore.js 1.8.3
   2 //     http://underscorejs.org
   3 //     (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
   4 //     Underscore may be freely distributed under the MIT license.
   5 //     中文注释 by hanzichi @https://github.com/hanzichi
   6 //     我的源码解读顺序(跟系列解读文章相对应)
   7 //     Object -> Array -> Collection -> Function -> Utility
   8 
   9 (function() {
  10 
  11   // Baseline setup
  12   // 基本设置、配置
  13   // --------------
  14 
  15   // Establish the root object, `window` in the browser, or `exports` on the server.
  16   // 将 this 赋值给局部变量 root
  17   // root 的值, 客户端为 `window`, 服务端(node) 中为 `exports`
  18   var root = this;
  19 
  20   // Save the previous value of the `_` variable.
  21   // 将原来全局环境中的变量 `_` 赋值给变量 previousUnderscore 进行缓存
  22   // 在后面的 noConflict 方法中有用到
  23   var previousUnderscore = root._;
  24 
  25   // Save bytes in the minified (but not gzipped) version:
  26   // 缓存变量, 便于压缩代码
  27   // 此处「压缩」指的是压缩到 min.js 版本
  28   // 而不是 gzip 压缩
  29   var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  30 
  31   // Create quick reference variables for speed access to core prototypes.
  32   // 缓存变量, 便于压缩代码
  33   // 同时可减少在原型链中的查找次数(提高代码效率)
  34   var
  35     push             = ArrayProto.push,
  36     slice            = ArrayProto.slice,
  37     toString         = ObjProto.toString,
  38     hasOwnProperty   = ObjProto.hasOwnProperty;
  39 
  40   // All **ECMAScript 5** native function implementations that we hope to use
  41   // are declared here.
  42   // ES5 原生方法, 如果浏览器支持, 则 underscore 中会优先使用
  43   var
  44     nativeIsArray      = Array.isArray,
  45     nativeKeys         = Object.keys,
  46     nativeBind         = FuncProto.bind,
  47     nativeCreate       = Object.create;
  48 
  49   // Naked function reference for surrogate-prototype-swapping.
  50   var Ctor = function(){};
  51 
  52   // Create a safe reference to the Underscore object for use below.
  53   // 核心函数
  54   // `_` 其实是一个构造函数
  55   // 支持无 new 调用的构造函数(思考 jQuery 的无 new 调用)
  56   // 将传入的参数(实际要操作的数据)赋值给 this._wrapped 属性
  57   // OOP 调用时,_ 相当于一个构造函数
  58   // each 等方法都在该构造函数的原型链上
  59   // _([1, 2, 3]).each(alert)
  60   // _([1, 2, 3]) 相当于无 new 构造了一个新的对象
  61   // 调用了该对象的 each 方法,该方法在该对象构造函数的原型链上
  62   var _ = function(obj) {
  63     // 以下均针对 OOP 形式的调用
  64     // 如果是非 OOP 形式的调用,不会进入该函数内部
  65 
  66     // 如果 obj 已经是 `_` 函数的实例,则直接返回 obj
  67     if (obj instanceof _)
  68       return obj;
  69 
  70     // 如果不是 `_` 函数的实例
  71     // 则调用 new 运算符,返回实例化的对象
  72     if (!(this instanceof _))
  73       return new _(obj);
  74 
  75     // 将 obj 赋值给 this._wrapped 属性
  76     this._wrapped = obj;
  77   };
  78 
  79   // Export the Underscore object for **Node.js**, with
  80   // backwards-compatibility for the old `require()` API. If we\'re in
  81   // the browser, add `_` as a global object.
  82   // 将上面定义的 `_` 局部变量赋值给全局对象中的 `_` 属性
  83   // 即客户端中 window._ = _
  84   // 服务端(node)中 exports._ = _
  85   // 同时在服务端向后兼容老的 require() API
  86   // 这样暴露给全局后便可以在全局环境中使用 `_` 变量(方法)
  87   if (typeof exports !== \'undefined\') {
  88     if (typeof module !== \'undefined\' && module.exports) {
  89       exports = module.exports = _;
  90     }
  91     exports._ = _;
  92   } else {
  93     root._ = _;
  94   }
  95 
  96   // Current version.
  97   // 当前 underscore 版本号
  98   _.VERSION = \'1.8.3\';
  99 
 100   // Internal function that returns an efficient (for current engines) version
 101   // of the passed-in callback, to be repeatedly applied in other Underscore
 102   // functions.
 103   // underscore 内部方法
 104   // 根据 this 指向(context 参数)
 105   // 以及 argCount 参数
 106   // 二次操作返回一些回调、迭代方法
 107   var optimizeCb = function(func, context, argCount) {
 108     // 如果没有指定 this 指向,则返回原函数
 109     if (context === void 0) return func;
 110 
 111     switch (argCount == null ? 3 : argCount) {
 112       case 1: return function(value) {
 113         return func.call(context, value);
 114       };
 115       case 2: return function(value, other) {
 116         return func.call(context, value, other);
 117       };
 118 
 119       // 如果有指定 this,但没有传入 argCount 参数
 120       // 则执行以下 case
 121       // _.each、_.map
 122       case 3: return function(value, index, collection) {
 123         return func.call(context, value, index, collection);
 124       };
 125 
 126       // _.reduce、_.reduceRight
 127       case 4: return function(accumulator, value, index, collection) {
 128         return func.call(context, accumulator, value, index, collection);
 129       };
 130     }
 131     return function() {
 132       return func.apply(context, arguments);
 133     };
 134   };
 135 
 136   // A mostly-internal function to generate callbacks that can be applied
 137   // to each element in a collection, returning the desired result — either
 138   // identity, an arbitrary callback, a property matcher, or a property accessor.
 139   var cb = function(value, context, argCount) {
 140     if (value == null) return _.identity;
 141     if (_.isFunction(value)) return optimizeCb(value, context, argCount);
 142     if (_.isObject(value)) return _.matcher(value);
 143     return _.property(value);
 144   };
 145 
 146   _.iteratee = function(value, context) {
 147     return cb(value, context, Infinity);
 148   };
 149 
 150   // An internal function for creating assigner functions.
 151   // 有三个方法用到了这个内部函数
 152   // _.extend & _.extendOwn & _.defaults
 153   // _.extend = createAssigner(_.allKeys);
 154   // _.extendOwn = _.assign = createAssigner(_.keys);
 155   // _.defaults = createAssigner(_.allKeys, true);
 156   var createAssigner = function(keysFunc, undefinedOnly) {
 157     // 返回函数
 158     // 经典闭包(undefinedOnly 参数在返回的函数中被引用)
 159     // 返回的函数参数个数 >= 1
 160     // 将第二个开始的对象参数的键值对 "继承" 给第一个参数
 161     return function(obj) {
 162       var length = arguments.length;
 163       // 只传入了一个参数(或者 0 个?)
 164       // 或者传入的第一个参数是 null
 165       if (length < 2 || obj == null) return obj;
 166 
 167       // 枚举第一个参数除外的对象参数
 168       // 即 arguments[1], arguments[2] ...
 169       for (var index = 1; index < length; index++) {
 170         // source 即为对象参数
 171         var source = arguments[index],
 172             // 提取对象参数的 keys 值
 173             // keysFunc 参数表示 _.keys
 174             // 或者 _.allKeys
 175             keys = keysFunc(source),
 176             l = keys.length;
 177 
 178         // 遍历该对象的键值对
 179         for (var i = 0; i < l; i++) {
 180           var key = keys[i];
 181           // _.extend 和 _.extendOwn 方法
 182           // 没有传入 undefinedOnly 参数,即 !undefinedOnly 为 true
 183           // 即肯定会执行 obj[key] = source[key]
 184           // 后面对象的键值对直接覆盖 obj
 185           // ==========================================
 186           // _.defaults 方法,undefinedOnly 参数为 true
 187           // 即 !undefinedOnly 为 false
 188           // 那么当且仅当 obj[key] 为 undefined 时才覆盖
 189           // 即如果有相同的 key 值,取最早出现的 value 值
 190           // *defaults 中有相同 key 的也是一样取首次出现的
 191           if (!undefinedOnly || obj[key] === void 0)
 192             obj[key] = source[key];
 193         }
 194       }
 195 
 196       // 返回已经继承后面对象参数属性的第一个参数对象
 197       return obj;
 198     };
 199   };
 200 
 201   // An internal function for creating a new object that inherits from another.
 202   // use in `_.create`
 203   var baseCreate = function(prototype) {
 204     // 如果 prototype 参数不是对象
 205     if (!_.isObject(prototype)) return {};
 206 
 207     // 如果浏览器支持 ES5 Object.create
 208     if (nativeCreate) return nativeCreate(prototype);
 209 
 210     Ctor.prototype = prototype;
 211     var result = new Ctor;
 212     Ctor.prototype = null;
 213     return result;
 214   };
 215 
 216   // 闭包
 217   var property = function(key) {
 218     return function(obj) {
 219       return obj == null ? void 0 : obj[key];
 220     };
 221   };
 222 
 223   // Helper for collection methods to determine whether a collection
 224   // should be iterated as an array or as an object
 225   // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
 226   // Avoids a very nasty ios 8 JIT bug on ARM-64. #2094
 227 
 228   // Math.pow(2, 53) - 1 是 javascript 中能精确表示的最大数字
 229   var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
 230 
 231   // getLength 函数
 232   // 该函数传入一个参数,返回参数的 length 属性值
 233   // 用来获取 array 以及 arrayLike 元素的 length 属性值
 234   var getLength = property(\'length\');
 235 
 236   // 判断是否是 ArrayLike Object
 237   // 类数组,即拥有 length 属性并且 length 属性值为 Number 类型的元素
 238   // 包括数组、arguments、HTML Collection 以及 NodeList 等等
 239   // 包括类似 {length: 10} 这样的对象
 240   // 包括字符串、函数等
 241   var isArrayLike = function(collection) {
 242     // 返回参数 collection 的 length 属性值
 243     var length = getLength(collection);
 244     return typeof length == \'number\' && length >= 0 && length <= MAX_ARRAY_INDEX;
 245   };
 246 
 247 
 248   // Collection Functions
 249   // 数组或者对象的扩展方法
 250   // 共 25 个扩展方法
 251   // --------------------
 252 
 253   // The cornerstone, an `each` implementation, aka `forEach`.
 254   // Handles raw objects in addition to array-likes. Treats all
 255   // sparse array-likes as if they were dense.
 256   // 与 ES5 中 Array.prototype.forEach 使用方法类似
 257   // 遍历数组或者对象的每个元素
 258   // 第一个参数为数组(包括类数组)或者对象
 259   // 第二个参数为迭代方法,对数组或者对象每个元素都执行该方法
 260   // 该方法又能传入三个参数,分别为 (item, index, array)((value, key, obj) for object)
 261   // 与 ES5 中 Array.prototype.forEach 方法传参格式一致
 262   // 第三个参数(可省略)确定第二个参数 iteratee 函数中的(可能有的)this 指向
 263   // 即 iteratee 中出现的(如果有)所有 this 都指向 context
 264   // notice: 不要传入一个带有 key 类型为 number 的对象!
 265   // notice: _.each 方法不能用 return 跳出循环(同样,Array.prototype.forEach 也不行)
 266   _.each = _.forEach = function(obj, iteratee, context) {
 267     // 根据 context 确定不同的迭代函数
 268     iteratee = optimizeCb(iteratee, context);
 269 
 270     var i, length;
 271 
 272     // 如果是类数组
 273     // 默认不会传入类似 {length: 10} 这样的数据
 274     if (isArrayLike(obj)) {
 275       // 遍历
 276       for (i = 0, length = obj.length; i < length; i++) {
 277         iteratee(obj[i], i, obj);
 278       }
 279     } else { // 如果 obj 是对象
 280       // 获取对象的所有 key 值
 281       var keys = _.keys(obj);
 282 
 283       // 如果是对象,则遍历处理 values 值
 284       for (i = 0, length = keys.length; i < length; i++) {
 285         iteratee(obj[keys[i]], keys[i], obj); // (value, key, obj)
 286       }
 287     }
 288 
 289     // 返回 obj 参数
 290     // 供链式调用(Returns the list for chaining)
 291     // 应该仅 OOP 调用有效
 292     return obj;
 293   };
 294 
 295   // Return the results of applying the iteratee to each element.
 296   // 与 ES5 中 Array.prototype.map 使用方法类似
 297   // 传参形式与 _.each 方法类似
 298   // 遍历数组(每个元素)或者对象的每个元素(value)
 299   // 对每个元素执行 iteratee 迭代方法
 300   // 将结果保存到新的数组中,并返回
 301   _.map = _.collect = function(obj, iteratee, context) {
 302     // 根据 context 确定不同的迭代函数
 303     iteratee = cb(iteratee, context);
 304 
 305     // 如果传参是对象,则获取它的 keys 值数组(短路表达式)
 306     var keys = !isArrayLike(obj) && _.keys(obj),
 307         // 如果 obj 为对象,则 length 为 key.length
 308         // 如果 obj 为数组,则 length 为 obj.length
 309         length = (keys || obj).length,
 310         results = Array(length); // 结果数组
 311 
 312     // 遍历
 313     for (var index = 0; index < length; index++) {
 314       // 如果 obj 为对象,则 currentKey 为对象键值 key
 315       // 如果 obj 为数组,则 currentKey 为 index 值
 316       var currentKey = keys ? keys[index] : index;
 317       results[index] = iteratee(obj[currentKey], currentKey, obj);
 318     }
 319 
 320     // 返回新的结果数组
 321     return results;
 322   };
 323 
 324   // Create a reducing function iterating left or right.
 325   // dir === 1 -> _.reduce
 326   // dir === -1 -> _.reduceRight
 327   function createReduce(dir) {
 328     // Optimized iterator function as using arguments.length
 329     // in the main function will deoptimize the, see #1991.
 330     function iterator(obj, iteratee, memo, keys, index, length) {
 331       for (; index >= 0 && index < length; index += dir) {
 332         var currentKey = keys ? keys[index] : index;
 333         // 迭代,返回值供下次迭代调用
 334         memo = iteratee(memo, obj[currentKey], currentKey, obj);
 335       }
 336       // 每次迭代返回值,供下次迭代调用
 337       return memo;
 338     }
 339 
 340     // _.reduce(_.reduceRight)可传入的 4 个参数
 341     // obj 数组或者对象
 342     // iteratee 迭代方法,对数组或者对象每个元素执行该方法
 343     // memo 初始值,如果有,则从 obj 第一个元素开始迭代
 344     // 如果没有,则从 obj 第二个元素开始迭代,将第一个元素作为初始值
 345     // context 为迭代函数中的 this 指向
 346     return function(obj, iteratee, memo, context) {
 347       iteratee = optimizeCb(iteratee, context, 4);
 348       var keys = !isArrayLike(obj) && _.keys(obj),
 349           length = (keys || obj).length,
 350           index = dir > 0 ? 0 : length - 1;
 351 
 352       // Determine the initial value if none is provided.
 353       // 如果没有指定初始值
 354       // 则把第一个元素指定为初始值
 355       if (arguments.length < 3) {
 356         memo = obj[keys ? keys[index] : index];
 357         // 根据 dir 确定是向左还是向右遍历
 358         index += dir;
 359       }
 360 
 361       return iterator(obj, iteratee, memo, keys, index, length);
 362     };
 363   }
 364 
 365   // **Reduce** builds up a single result from a list of values, aka `inject`,
 366   // or `foldl`.
 367   // 与 ES5 中 Array.prototype.reduce 使用方法类似
 368   // _.reduce(list, iteratee, [memo], [context])
 369   // _.reduce 方法最多可传入 4 个参数
 370   // memo 为初始值,可选
 371   // context 为指定 iteratee 中 this 指向,可选
 372   _.reduce = _.foldl = _.inject = createReduce(1);
 373 
 374   // The right-associative version of reduce, also known as `foldr`.
 375   // 与 ES5 中 Array.prototype.reduceRight 使用方法类似
 376   _.reduceRight = _.foldr = createReduce(-1);
 377 
 378   // Return the first value which passes a truth test. Aliased as `detect`.
 379   // 寻找数组或者对象中第一个满足条件(predicate 函数

以上是关于underscore-1.8.3-analysis.js的主要内容,如果未能解决你的问题,请参考以下文章