underscore.js 170 -- 293 行
来源:互联网 发布:八爪鱼数据采集能干嘛 编辑:程序博客网 时间:2024/06/03 15:23
// The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { iteratee = optimizeCb(iteratee, context); var i, length; if (isArrayLike(obj)) { for (i = 0, length = obj.length; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; // If it's totally an object }; //说明item不影响数组或者伪数组本身 // Return the results of applying the iteratee to each element. _.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; }; // Create a reducing function iterating left or right. var createReduce = function(dir) { // Wrap code that reassigns argument variables in a separate function than // the one that accesses `arguments.length` to avoid a perf hit. (#1991) var reducer = function(obj, iteratee, memo, initial) { var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1; if (!initial) { memo = obj[keys ? keys[index] : index]; index += dir; } for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; return function(obj, iteratee, memo, context) { var initial = arguments.length >= 3; return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); }; }; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = createReduce(1); // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = createReduce(-1); // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; var key = keyFinder(obj, predicate, context); if (key !== void 0 && key !== -1) return obj[key]; }; // Return all the elements that pass a truth test. // Aliased as `select`. _.filter = _.select = function(obj, predicate, context) { var results = []; predicate = cb(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(cb(predicate)), context); }; // Determine whether all of the elements match a truth test. // Aliased as `all`. _.every = _.all = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; // Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false; }; // Determine if the array or object contains a given item (using `===`). // Aliased as `includes` and `include`. _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { if (!isArrayLike(obj)) obj = _.values(obj); if (typeof fromIndex != 'number' || guard) fromIndex = 0; return _.indexOf(obj, item, fromIndex) >= 0; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = restArgs(function(obj, path, args) { var contextPath, func; if (_.isFunction(path)) { func = path; } else if (_.isArray(path)) { contextPath = path.slice(0, -1); path = path[path.length - 1]; } return _.map(obj, function(context) { var method = func; if (!method) { if (contextPath && contextPath.length) { context = deepGet(context, contextPath); } if (context == null) return void 0; method = context[path]; } return method == null ? method : method.apply(context, args); }); });
终于到了函数各个功能实现的部分了,先回顾一下之前我们看到的准备函数:
1. getLength (获取所有跟length有关的属性)
2. isArrayLike (该对象是否跟数组类似,拥有长度属性,并且在0到最大数组长之内)
3. baseCreate (继承原型所用)
4. cb (用以创造一个新的迭代函数,根据不同的情况进行处理,如果第一个传入参数是函数,则等同与optimizeCb处理)
5. optmizeCb (优化函数,绑定函数this,并且规定传入参数的数量)
_.each
_.each = _.forEach = function(obj, iteratee, context) { iteratee = optimizeCb(iteratee, context); var i, length; if (isArrayLike(obj)) { for (i = 0, length = obj.length; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; };
_.each没什么好说的,就是在迭代器中传入对象每一个值,
不同情况不同的处理方式,伪数组或者数组则处理1,2,3,4的部分,如果是一个对象,则遍历keys。
_.map
_.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; };
相当于在内部先创造一个空数组,然后在当中分别对应迭代器返回的值,迭代器传入参数分别为:每个值,index,对象本身,然后返回这个空数组
从代码中可以看出,_.each用作纯遍历工具比该方法更加高效。
_.reduce
// Create a reducing function iterating left or right. var createReduce = function(dir) { // Wrap code that reassigns argument variables in a separate function than // the one that accesses `arguments.length` to avoid a perf hit. (#1991) var reducer = function(obj, iteratee, memo, initial) { var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1; if (!initial) { memo = obj[keys ? keys[index] : index]; index += dir; } for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; return function(obj, iteratee, memo, context) { var initial = arguments.length >= 3; return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); }; }; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = createReduce(1); // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = createReduce(-1);
我们知道reduce的就是
_.reduce(obj, function () {}, accumulator, context)
其中这个
var createReduce = function(dir) { // Wrap code that reassigns argument variables in a separate function than // the one that accesses `arguments.length` to avoid a perf hit. (#1991) var reducer = function(obj, iteratee, memo, initial) { var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1; if (!initial) { memo = obj[keys ? keys[index] : index]; index += dir; } for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; return function(obj, iteratee, memo, context) { var initial = arguments.length >= 3; return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); }; };
即为构造器,构造reduce起点或者终点,dir参数正负决定方向,大小决定遍历的方式
memo代表之前存放的数值,如未规定,则默认为开头
迭代器中的参数大概为:memo, currentItem, currentIndex, obj
然后再return一个新的memo出来
完全可以把这个函数简化点:
var createReduce = function (dir) { function reduce (obj, iteratee, memo, initial) { var index = 0 var length = obj.length if (!initial) { memo = obj[index] index+= dir } for (; index >=0 && index < length; index += dir) { var currentItem = obj[index] memo = iteratee(memo, currentItem, index, obj) } return memo } return function (obj, iteratee, memo) { var initial = arguments.length >= 3 return reduce(obj, iteratee, memo, initial) }}
其中initial = arguments.length >= 3用于自动处理当规定memo后,reduce自动将其initial为true
那么reduce就方便了:
_.reduce = _.foldl = _.inject = createReduce(1); _.reduceRight = _.foldr = createReduce(-1);
_.find
_.find = _.detect = function(obj, predicate, context) { var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; var key = keyFinder(obj, predicate, context); if (key !== void 0 && key !== -1) return obj[key]; };
看到这个函数。 第一眼就发现一个.findIndex和.findKey
估计是在后面出现的,于是搜索一下找到这两个函数:
_.findIndex = createPredicateIndexFinder(1); _.findKey = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = _.keys(obj), key; for (var i = 0, length = keys.length; i < length; i++) { key = keys[i]; if (predicate(obj[key], key, obj)) return key; } };var createPredicateIndexFinder = function(dir) { return function(array, predicate, context) { predicate = cb(predicate, context); var length = getLength(array); var index = dir > 0 ? 0 : length - 1; for (; index >= 0 && index < length; index += dir) { if (predicate(array[index], index, array)) return index; } return -1; }; };
我们先看findKey:
_.findKey = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = _.keys(obj), key; for (var i = 0, length = keys.length; i < length; i++) { key = keys[i]; if (predicate(obj[key], key, obj)) return key; } };
我们可以发现当中的predicate是个用来作判断的函数,如果其返回值为true, 则直接返回第一个符合条件的索引,其中向predicate传入的参数为item, key, obj
再看_.findIndex:
_.findIndex = createPredicateIndexFinder(1);var createPredicateIndexFinder = function(dir) { return function(array, predicate, context) { predicate = cb(predicate, context); var length = getLength(array); var index = dir > 0 ? 0 : length - 1; for (; index >= 0 && index < length; index += dir) { if (predicate(array[index], index, array)) return index; } return -1; }; };
没得说,跟我们刚才那个createReduce的dir作用一样,正负决定方向,dir参数大小决定方式,同样return一个新的function,找到符合条件的立即返回,若找不到则返回-1
回到_.find:
_.find = _.detect = function(obj, predicate, context) { var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; var key = keyFinder(obj, predicate, context); if (key !== void 0 && key !== -1) return obj[key]; };
我们发现这是对对象和数组类型两种不同情况更高一层的封装,最底层的方法当然是createPredicateIndexFinder与_.findKey这个可能较为重要,以后可能会继续用到。
_.filter
_.filter = _.select = function(obj, predicate, context) { var results = []; predicate = cb(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; };
果不其然,之前类似的方法我们又得用到一次,只不过这回内部先造出了一个新的数组,然后用.each遍历我们的obj,然后用函数判断是否符合要求,是则将其推入数组,最终返回一个数组,看来.each是最高效的一个遍历方法,也更底层一些。
_.reject
// Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(cb(predicate)), context); };
这个么,
_.negate = function(predicate) { return function() { return !predicate.apply(this, arguments); }; };
说白了就是将predicate的判断结果反过来
同理reject就是说将所有满足条件的值全部弹出,保留下不满足条件的值。
_.every
_.every = _.all = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; };
从内部的for循环我们可以看出,一旦obj内发现任何不满足条件的值,就返回false,如果obj内的所有值遵纪守法最终就返回true
_.some
// Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false; };
我们发现,这个函数与.every/.all最大的区别就是。。。。return的值反了过来,也就是说,一旦有任何函数满足条件,就返回true,都不满足最终会返回false
感觉这个用_.any比较合适,因为some实在是不能立即判断出这个函数的作用。
_.contains
// Determine if the array or object contains a given item (using `===`). // Aliased as `includes` and `include`. _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { if (!isArrayLike(obj)) obj = _.values(obj); if (typeof fromIndex != 'number' || guard) fromIndex = 0; return _.indexOf(obj, item, fromIndex) >= 0; };
真是要吐了,怎么又出现了奇怪的函数。。。能在这好好写不行吗?
没办法,开搜
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); // _.findIndex我们已经知道就是返回第一个满足条件的值的index_.sortedIndex = function(array, obj, iteratee, context) { iteratee = cb(iteratee, context, 1); var value = iteratee(obj); var low = 0, high = getLength(array); while (low < high) { var mid = Math.floor((low + high) / 2); if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } return low; }; //二分搜索,输入的值需要为升序,返回的值为第一个比条件数字大的值的索引 ```
然后就是createIndexFinder:
该方法参考网址
// Generator function to create the indexOf and lastIndexOf functions // _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); // _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); function createIndexFinder(dir, predicateFind, sortedIndex) { // API 调用形式 // _.indexOf(array, value, [isSorted]) // _.indexOf(array, value, [fromIndex]) // _.lastIndexOf(array, value, [fromIndex]) return function(array, item, idx) { var i = 0, length = getLength(array); // 如果 idx 为 Number 类型 // 则规定查找位置的起始点 // 那么第三个参数不是 [isSorted] // 所以不能用二分查找优化了 // 只能遍历查找 if (typeof idx == 'number') { if (dir > 0) { // 正向查找 // 重置查找的起始位置 i = idx >= 0 ? idx : Math.max(idx + length, i); } else { // 反向查找 // 如果是反向查找,重置 length 属性值 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; } } else if (sortedIndex && idx && length) { // 能用二分查找加速的条件 // 有序 & idx !== 0 && length !== 0 // 用 _.sortIndex 找到有序数组中 item 正好插入的位置 idx = sortedIndex(array, item); // 如果正好插入的位置的值和 item 刚好相等 // 说明该位置就是 item 第一次出现的位置 // 返回下标 // 否则即是没找到,返回 -1 return array[idx] === item ? idx : -1; } // 特判,如果要查找的元素是 NaN 类型 // 如果 item !== item // 那么 item => NaN if (item !== item) { idx = predicateFind(slice.call(array, i, length), _.isNaN); return idx >= 0 ? idx + i : -1; } // O(n) 遍历数组 // 寻找和 item 相同的元素 // 特判排除了 item 为 NaN 的情况 // 可以放心地用 `===` 来判断是否相等了 for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { if (array[idx] === item) return idx; } return -1; }; }
回到_.contains
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { if (!isArrayLike(obj)) obj = _.values(obj); if (typeof fromIndex != 'number' || guard) fromIndex = 0; return _.indexOf(obj, item, fromIndex) >= 0; };
这中间guard的参数意义也就变得明了,即保证传入的obj是升序的,则此时会用二分法查找,效率会变得更高。
总结:createPredicateIndex –> .indexof –> .contains
一步步封装,第一层是构造一个寻找函数,第二层则是对应不同情况进行加强修补调用,第三层则更是简化传入参数步骤。
- underscore.js 170 -- 293 行
- underscore.js 102 -- 170行
- underscore.js 293--409
- underscore.js 409 -- 526行
- underscore.js 530 -- 652行
- underscore.js 652 --- 748行
- underscore.js 964 --- 1103行
- underscore.js 1105 -- 1288 行
- Underscore.js
- underscore.js
- Underscore.js
- underscore.js
- underscore.js
- underscore.js 0 -- 101行
- underscore.js学习
- [转]Underscore.js用法
- Underscore.js笔记
- Underscore.js-template模板
- BZOJ 4013: [HNOI2015]实验比较
- PHP性能分析,测试
- 关于安卓开发的一些tips(持续更新)
- 阿里云Centos 7.5安装Mysql(命令行)
- 如何区别指针数组和数组指针?
- underscore.js 170 -- 293 行
- 合并两个有序的链表和计算1+2+3+4....
- oracle 将查询出来的值赋值给变量
- 爬虫工作原理
- 2598【ZJOI2010 Day1】网络扩容
- iOS 7.0 以上一些弃用的系统API替换
- 未能加载文件或程序集“SrvCommon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”
- Logisitc Regrssion vs linear SVM
- 开通博客