接上一节迭代器的话题,在打造underscore系列的第三篇中,咱们引入了迭代器的感念,并实现了reduce, map, times等经常使用的迭代器方法,因为篇幅过长,咱们将其余迭代器的实现放在了这一小节中。算法
_.each 和 _.foreach 方法本质上是定义的同一个方法,在打造属于本身的underscore系列 ( 一 )的框架原理中,咱们对 _.each的方法作了实现,其中call改变this指向的代码咱们是这样实现的。数组
if (_.isArray(target)) {
var length = target.length;
for (; i < length; i++) {
callback.call(target, target[i], i);
}
} else {
for (key in target) {
callback.call(target, key, target[key]);
}
}
复制代码
而在系列三小节内容中,咱们优化了this指向这部分代码,具体封装为optimizeCb 函数,详见打造属于本身的underscore系列 ( 三 ),这里不作重复赘述。通过迭代器优化后,each的实现以下, 其中迭代器参数类型为3,即 func.call(context, value, index)
bash
// 遍历 数组 对象
_.each = _.forEach = function (target, callback, context) {
iteratee = optimizeCb(callback, context, 3) // 迭代器优化
var key, i = 0;
if (_.isArray(target)) {
var length = target.length;
for (; i < length; i++) {
iteratee(target[i], i);
}
} else {
for (key in target) {
iteratee(key, itarget[key])
}
}
}
复制代码
与原生js中some的方法相同,some的迭代器会遍历元素,同时进行真值检测,一旦有元素经过真值检测,则返回true。所以,咱们首先须要了解什么是真值检测? 简单的理解,真值检测就是条件判断,即将检测的值转换Boolean类型,判断转换后的值是否为true。而在条件判断中,咱们作了一个总结,除了 undefined, null, false, NaN, '', 0, -0
,其余值都会转换为true, 包括空对象。eg框架
if(null) {console.log('null')}
if(0) {console.log(2)}
if('222'){ console.log('string')}
if(false) { console.log(false)}
if(NaN) { console.log('nan')}
if(undefined) { console.log('undefined')}
if('') { console.log('空')}
if(-0) { console.log('-0')}
if({}) { console.log('object')}
// 'string', 'object'
复制代码
一样遍历的list能够为对象或者数组,所以相似的_some的实现能够类比以前的例子。ide
_.some = _.any = function(list, iteratee, context) {
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
for(var index = 0; index < length; index++ ) {
var currentKey = keys? keys[index] : index;
if(currentKey) return true
}
return false
}
复制代码
some方法和map方法在使用方式上的相同点在于,函数的第二个参数iteratee不单单局限于函数,还能够不传值,或者传递对象等,所以一样的处理方式咱们能够用前一节总结的回调函数进行优化。函数
// 系列的第三节总结的cb方法
var cb = function (iteratee, context, args) {
if (iteratee == null) return _.identity;
if (_.isFunction(iteratee)) return optimizeCb(iteratee, context, args); //optimizeCb优化迭代器
if (_.isObject(iteratee) && !_.isArray(value)) return _.matcher(value);
//其余
}
_.some = _.any = function(list, iteratee, context) {
predicate = cb(iteratee, context, 3)
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
for(var index = 0; index < lengths; index++ ) {
var currentKey = keys? keys[index] : index;
if(predicate(list[currentKey], currentKey, list)) return true // 只要某一项的真值检测经过,则中止遍历,返回true,全部都不经过时,结果才为false
}
return false
}
复制代码
和some方法相反,every方法会遍历list并进行真值检测,只有当全部检测值返回true时,结果才会返回true。有了some实现为基础,every方法的实现显得格外简单post
_.every = _.all = function(list, iteratee, context) {
predicate = cb(iteratee, context, 3)
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
for(var index = 0; index < lengths; index++ ) {
var currentKey = keys? keys[index] : index;
if(!predicate(list[currentKey], currentKey, list)) return false;// 当某一项真值检测不经过时,则中止遍历,返回false,只有全部值都经过时,结果才为true
}
return true
}
复制代码
filter方法一样会对list进行遍历,并对每一个遍历元素进行真值判断,和every,some不一样的是,filter会将全部经过真值检测的元素组成一个新的数组返回。所以,只要简单的处理真值判断后的语句便可完成filter的功能优化
_.filter = _.select = function(list, iteratee, context) {
predicate = cb(iteratee, context, 3)
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
var results = [];
for(var index = 0; index < lengths; index++ ) {
var currentKey = keys? keys[index] : index;
if(predicate(list[currentKey], currentKey, list)) results.push(list[currentKey])
}
return results
}
复制代码
至此,underscore中关于迭代器相关方法的应用场景和实现思路基本介绍完毕。咱们知道,迭代器在平常代码逻辑编写中必不可少,且使用频率较高。熟练掌握迭代器的应用场景并深刻理解其中实现思路有利于现实复杂的业务功能的实现。ui