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的主要内容,如果未能解决你的问题,请参考以下文章