本文同步自我得博客:http://www.joeray61.com
最近十几天都在忙毕业论文的事,所以上一次为大家介绍完underscore这个框架的结构(或者说是这个框架的设计思路)之后就一直没动静了,今天我又满血复活了,让我们继续来探索underscore的源码奥秘吧。
没看过上一篇文章的朋友可以戳这里:underscore源码解析(一)
今天的内容是underscore里面封装的一些函数,我将逐个介绍,咱们直接入正题吧
each / _.each / _.forEach
var each = _.each = _.forEach = function(obj, iterator, context) {
// 不处理空值
if(obj == null)
return;
if(nativeForEach && obj.forEach === nativeForEach) {
// 如果宿主环境支持, 则优先调用JavaScript 1.6提供的forEach方法
obj.forEach(iterator, context);
} else if(obj.length === +obj.length) {
// 对[数组]中每一个元素执行处理器方法
for(var i = 0, l = obj.length; i < l; i++) {
if( i in obj && iterator.call(context, obj[i], i, obj) === breaker)
return;
}
} else {
// 对{对象}中每一个元素执行处理器方法
for(var key in obj) {
if(_.has(obj, key)) {
if(iterator.call(context, obj[key], key, obj) === breaker)
return;
}
}
}
};
这个函数的实现思想其实很简单,如果宿主环境(一般为浏览器或者node.js)支持原生的forEach方法,就调用原生的,否则就遍历该数组或者对象,依次调用处理器方法
值得一提的是在判断是否是数组的时候,这里的代码为
obj.length === +obj.length
这其实是一种鸭式辨型的判定方法,具体可以参见我在SF上提过的一个问题:点我
_.map / _.collect
_.map = _.collect = function(obj, iterator, context) {
// 用于存放返回值的数组
var results = [];
if(obj == null)
return results;
// 优先调用宿主环境提供的map方法
if(nativeMap && obj.map === nativeMap)
return obj.map(iterator, context);
// 迭代处理集合中的元素
each(obj, function(value, index, list) {
// 将每次迭代处理的返回值存储到results数组
results[results.length] = iterator.call(context, value, index, list);
});
// 返回处理结果
if(obj.length === +obj.length)
results.length = obj.length;
return results;
};
map/collect函数与each的区别在于map/collect会存储每次迭代的返回值, 并作为一个新的数组返回
_.reduce / _.foldl / _.inject
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
// 通过参数数量检查是否存在初始值
var initial = arguments.length > 2;
if(obj == null)
obj = [];
// 优先调用宿主环境提供的reduce方法
if(nativeReduce && obj.reduce === nativeReduce && false) {
if(context)
iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
// 迭代处理集合中的元素
each(obj, function(value, index, list) {
if(!initial) {
// 如果没有初始值, 则将第一个元素作为初始值; 如果被处理的是对象集合, 则默认值为第一个属性的值
memo = value;
initial = true;
} else {
// 记录处理结果, 并将结果传递给下一次迭代
memo = iterator.call(context, memo, value, index, list);
}
});
if(!initial)
throw new TypeError('Reduce of empty array with no initial value');
return memo;
};
这个函数的作用是将集合中每个元素放入迭代处理器, 并将本次迭代的返回值作为memo传递到下一次迭代, 一般用于累计结果或连接数据
_.reduceRight / _.foldr
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if(obj == null)
obj = [];
// 优先调用宿主环境提供的reduceRight方法
if(nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if(context)
iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
// 逆转集合中的元素顺序
var reversed = _.toArray(obj).reverse();
if(context && !initial)
iterator = _.bind(iterator, context);
// 通过reduce方法处理数据
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
};
这个函数与reduce相似,不过它是逆向迭代集合中的元素
_.find / _.detect
_.find = _.detect = function(obj, iterator, context) {
// result存放第一个能够通过验证的元素
var result;
// 通过any方法遍历数据, 并记录通过验证的元素
any(obj, function(value, index, list) {
// 如果处理器返回的结果被转换为Boolean类型后值为true, 则记录当前值并返回当前元素
if(iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
这个方法的作用是遍历集合中的元素, 返回能够通过处理器验证的第一个元素
_.filter / _.select
_.filter = _.select = function(obj, iterator, context) {
// 用于存储通过验证的元素数组
var results = [];
if(obj == null)
return results;
// 优先调用宿主环境提供的filter方法
if(nativeFilter && obj.filter === nativeFilter)
return obj.filter(iterator, context);
// 迭代集合中的元素, 并将通过处理器验证的元素放到数组中并返回
each(obj, function(value, index, list) {
if(iterator.call(context, value, index, list))
results[results.length] = value;
});
return results;
};
这个方法与find作用类似, 但它会记录下集合中所有通过验证的元素
_.reject
_.reject = function(obj, iterator, context) {
var results = [];
if(obj == null)
return results;
each(obj, function(value, index, list) {
if(!iterator.call(context, value, index, list))
results[results.length] = value;
});
return results;
};
这个方法的代码里面我没有加注释,因为整个代码与filter/select方法几乎一样,不同点在于向results数组里添加元素的时候判断条件是相反的,也就是说这个方法的作用是返回没有通过处理器验证的元素列表
_.every / _.all
_.every = _.all = function(obj, iterator, context) {
var result = true;
if(obj == null)
return result;
// 优先调用宿主环境提供的every方法
if(nativeEvery && obj.every === nativeEvery)
return obj.every(iterator, context);
// 迭代集合中的元素
each(obj, function(value, index, list) {
// 这里我不太理解,为什么药写成 result = (result && iterator.call(context, value, index, list)) 而不是 result = iterator.call(context, value, index, list)
if(!( result = result && iterator.call(context, value, index, list)))
return breaker;
});
return !!result;
};
这个方法的作用是如果集合中所有元素均能通过处理器验证, 则返回true
any / _.some / _.any
var any = _.some = _.any = function(obj, iterator, context) {
// 如果没有指定处理器参数, 则使用默认的处理器函数,该函数会返回参数本身
iterator || ( iterator = _.identity);
var result = false;
if(obj == null)
return result;
// 优先调用宿主环境提供的some方法
if(nativeSome && obj.some === nativeSome)
return obj.some(iterator, context);
// 迭代集合中的元素
each(obj, function(value, index, list) {
if(result || ( result = iterator.call(context, value, index, list)))
return breaker;
});
return !!result;
};
该函数的作用是检查集合中是否有任何一个元素在被转换成Boolean类型时是否为true
_.include / _.contains
_.include = _.contains = function(obj, target) {
var found = false;
if(obj == null)
return found;
// 优先调用宿主环境提供的Array.prototype.indexOf方法
if(nativeIndexOf && obj.indexOf === nativeIndexOf)
return obj.indexOf(target) != -1;
// 通过any方法迭代集合中的元素, 验证元素的值和类型与目标是否完全匹配
found = any(obj, function(value) {
return value === target;
});
return found;
};
这个函数用于检查集合中是否有值与目标参数完全匹配,包括数据类型
小结
今天先介绍以上10个函数的实现细节,之后还会继续带来其他函数的介绍,欢迎大家提出指正和建议,thx for reading, hope u enjoy