前言Underscore.js源码解析第三部分,前两个地址是那些不起眼的小玩意儿?(void0)andundefined本文原链接的源码地址?看了很多技术文章,还是写不好前端。从开始踏入程序员的大坑到现在,翻阅了无数的技术文章和书籍,有的是零散的知识,有的是一系列的权威教程,却写不出我心爱的前端——毛泽东结束。听过一句话,就是说,这个世界上,深爱你的人,并不是只有你一个人。但即便如此,我还是想在技术的道路上一路走黑。直到天涯迷路,海角翻船。今天想说说我们平时工作中经常用到的几个宝物。让我们称他为杀手,因为它真的很容易上手。他们是...eachmapreducereduceRightfindfiltereverysome接下来从underscore.js的角度,一步步看他们的内部运行原理...1_.each(list,iteratee,[context])遍历list中的所有元素,并按顺序使用它们遍历并输出每个元素,如果传递了上下文,则将iteratee函数中的this绑定到上下文。下面看看如何使用letarr=['name','sex']letobj={name:'qianlongo',sex:'boy'}//不传入context//遍历数组_。each(arr,console.log)//name0(2)["name","sex"]//sex1(2)["name","sex"]//遍历objects_.each(obj,console.log)//qianlongoname{name:"qianlongo",sex:"boy"}//男孩性别{name:"qianlongo",sex:"boy"}//传入context_.each(arr,function(val,key,arr){console.log(this[val])},obj)//qianlongo//小子可以看出下划线的each和原生数组forEach有些相似又不同。原生的forEach只能遍历数组,而下划线的each也可以遍历对象。接下来是不是想看看下划线是如何实现的。快点!!!源码_.each=_.forEach=function(obj,iteratee,context){//优化遍历函数iteratee,将iteratee中的this动态设置为contextiteratee=optimizeCb(iteratee,context);vari,长度;if(isArrayLike(obj)){//如果是类数组objfor(i=0,length=obj.length;i{return`hello:${val}`})//["hello:qianlongo","hello:boy"]//list是一个对象_.map(obj,(val,key,obj)=>{return`hello:${val}`})//["hello:qianlongo","hello:boy"]当然也可以传入第三个参数context,它的本质就像each,iteratee函数中的this也是动态设置上下文源代码_.map=_.collect=function(obj,iteratee,context){//这里的内部cb函数可以理解为将iteratee的this绑定到context上iteratee=cb(iteratee,context);//obj的键是为非类数组对象获取的。如果是类数组对象,最终得到的键是未定义的//创建一个与obj长度空间相同的数组for(varindex=0;index{returnval>0})//falseletresult2=_.every(obj,(val,key,obj)=>{returnval.indexOf('o')>-1})//true使用起来非常简单,传入一个谓词函数(返回值为布尔值的函数),最后得到true或false。源码_.every=_.all=function(obj,predicate,context){//这里的内部cb函数可以理解为将iteratee的this绑定到contextpredicate=cb(predicate,context);//短路写法,获取非类数组的keyvarkeys=!isArrayLike(obj)&&_.keys(obj),length=(keys||obj).length;for(varindex=0;index{returnval>0})//true因为至少有一个元素>0letresult2=_.some(obj,(val,key,obj)=>{returnval.indexOf('o')>-1})//true两者都包含'o'当然,源码中是如何返回true的,和every的唯一区别就是返回true还是false?源代码_.some=_.any=function(obj,predicate,context){predicate=cb(predicate,context);varkeys=!isArrayLike(obj)&&_.keys(obj),length=(keys||obj).length;for(varindex=0;index{returnval>0})//3letresult2=_.find(obj,(val,key,obj)=>{returnval.indexOf('o')>-1})//男孩源代码_.find=_.detect=function(obj,predicate,context){varkey;if(isArrayLike(obj)){//当输入为类数组时,调用findIndex方法,结果为>=-1arraykey=_.findIndex(obj,predicate,context);}else{//当传入一个对象时,调用findKey,结果为字符串属性或undefinedkey=_.findKey(obj,predicate,context);}//返回满足条件的值,否则没有返回值,即默认undefinedif(key!==void0&&key!==-1)returnobj[key];};_.findIndex和_.findKey后面会一一分析。目前,了解find函数并知道如何使用它们就足够了。6_.filter(list,predicate,[context])遍历list,返回predicate检测到的所有元素(结果为数组)用例letarr=[-1,-3,-6,0,3,6,9]letobj={sex:'boy',name:'qianlongo',age:100}letresult=_.filter(arr,(val,key,arr)=>{returnval>0})//[3,6,9]letresult2=_.filter(obj,(val,key,obj)=>{return`${val}`.indexOf('o')>-1//使用模板字符串就是为了防止100没有indexOf方法而报错})//["boy","qianlongo"]聪明的你搞清楚源码是怎么实现的了吗?源代码_.filter=_.select=function(obj,predicate,context){varresults=[];//将谓词的this作用域绑定到上下文predicate=cb(predicate,context);//使用each方法遍历obj_.each(obj,function(value,index,list){//如果满足predicate的过滤条件,则将对应的值放入results数组中if(predicate(value,index,列表))results.push(value);});返回结果;//最后返回};最后是reduce和reduceRight,两个相对比较难的api,虽然已经过了12点,手动困了?让我们咬紧牙关,坚持最后两个。[memo],[context]),别名为inject和foldl,reduce方法将列表中的元素缩减为单个值。Memo是reduce函数的初始值,reduce的每一步都需要iteratee返回。本次迭代传递4个参数:memo、value和迭代的索引(或键)以及最后一个引用的整个list8_.reduceRight(list,iteratee,memo,[context])reduceRight是元素组合的reduce从右边开始函数用例vararr=[0,1,2,3,4,5],sum=_.reduce(arr,(init,cur,i,arr)=>{returninit+cur;});//15我们来看看上面的执行过程是怎样的。第一轮//因为没有传入initialValue,所以回调函数的第一个参数是数组的第一项init=0;当前=1;=>初始化+当前=1;第二轮init=1;当前=2;=>初始化+当前=3;第三轮init=3;当前=3;=>初始化+当前=6;第四轮init=6;当前=4;=>初始化+当前=10;第五轮init=10;当前=5;=>初始化+当前=15;?妈妈,行刑终于结束了,打了这么多轮才结束。不像是搏击高手,一下子就把太极宗师挂了,知道一步步执行的过程。我们来看看源码是如何实现的。源码//源码仍然是调用createReduce生成的,所以主要依赖createReduce函数_.reduce=_.foldl=_.inject=createReduce(1);这该死的看起来很可怕,不要害怕,让我们来做一点分析functioncreateReduce(dir){//优化的迭代器函数,因为在main函数中使用arguments.length//将取消优化,参见#1991。functioniterator(obj,iteratee,memo,keys,index,length){//true迭代执行的位置for(;index>=0&&index0?0:长度-1;//default开始迭代的位置左边第一个或右边第一个//如果没有提供,则确定初始值。if(arguments.length<3){//如果没有传入初始化值,则第一个值(左边第一个或右边第一个)作为初始值memo=obj[keys?键[索引]:索引];索引+=目录;//从索引1或索引长度-2开始迭代}returniterator(obj,iteratee,memo,keys,index,length);//然后开始进入自定义迭代函数,请查收};}结束语夜深人静,有点困了,希望这篇文章对大家有用。如果对之前的源码分析文章感兴趣,请到置顶地址查看。不介意的话,在文章开头的源码地址点个小星星好吗?不介意的话,在文章开头的源码地址点个小星星。?不介意的话在文章开头的源码地址点个小星星好吗?