本文同步自我得博客:http://www.joeray61.comnode
最近十几天都在忙毕业论文的事,因此上一次为你们介绍完underscore这个框架的结构(或者说是这个框架的设计思路)以后就一直没动静了,今天我又满血复活了,让咱们继续来探索underscore的源码奥秘吧。
没看过上一篇文章的朋友能够戳这里:underscore源码解析(一)
今天的内容是underscore里面封装的一些函数,我将逐个介绍,我们直接入正题吧segmentfault
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 = 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会存储每次迭代的返回值, 并做为一个新的数组返回ide
_.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 = 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类似,不过它是逆向迭代集合中的元素prototype
_.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 = 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做用相似, 但它会记录下集合中全部经过验证的元素3d
_.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 = 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
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 = 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