underscore源码解析(集合)
2015-11-16 17:25
211 查看
underscore就是一些函数的集合,大概分为6部分,分别是基础函数,集合,数组,函数,对象,实用功能. 在实现每一个功能的时候,基本上先自己实现,再对比。
(function(){ }.call(this)); 基本框架长这样,就一个函数的自调用,用window作为调用对象,防止污染全局。
//把window对象先存起来备用吧。 varroot=this; //弄个window的属性_; varpreviousUnderscore=root._; //存一些原型 varArrayProto=Array.prototype,ObjProto=Object.prototype,FuncProto=Function.prototype; //存一些原型的方法 var push=ArrayProto.push, slice=ArrayProto.slice, toString=ObjProto.toString, hasOwnProperty=ObjProto.hasOwnProperty; var nativeIsArray=Array.isArray, nativeKeys=Object.keys, nativeBind=FuncProto.bind, nativeCreate=Object.create; //弄个备用的构造函数 varCtor=function(){}; //构造函数_,obj是_实例的话,直接返回obj.若是直接调用构造函数而不是通过new操作符的话,会调用第二个if.执行new_(obj). //另外,这样的话obj就不是实例了,所以弄个实例属性_wrapped把obj存起来备用。 var_=function(obj){ if(objinstanceof_)returnobj; if(!(thisinstanceof_))returnnew_(obj); this._wrapped=obj; }; //这个是什么nodejs接口的,不懂。过了,嘿嘿
if(typeofexports!=='undefined'){ if(typeofmodule!=='undefined'&&module.exports){ exports=module.exports=_; } exports._=_; }else{ root._=_; }
//这个是关于函数优化的,参数函数,调用EC,参数数量。 //只传了一个参数的话,context==null.这里应该是可以用context===void0或者context==null,一个意思。直接返回传入的函数 //没传第三个参数的话,默认就是传三个参数给调用的函数,一般是用在forEach,filter这些数组方法,function(element,index,array); //第三个参数是1或2的话,感觉没什么特别的,直接调用吧.是4的话就是reduce函数之类的。 varoptimizeCb=function(func,context,argCount){ if(context===void0)returnfunc; switch(argCount==null?3:argCount){ case1:returnfunction(value){ returnfunc.call(context,value); }; case2:returnfunction(value,other){ returnfunc.call(context,value,other); }; case3:returnfunction(value,index,collection){ returnfunc.call(context,value,index,collection); }; case4:returnfunction(accumulator,value,index,collection){ returnfunc.call(context,accumulator,value,index,collection); }; } returnfunction(){ returnfunc.apply(context,arguments); }; };
//没传参数的话,直接返回一个函数,设置a,调用这个函数a,返回传给a的参数 //value是函数的话,直接调用上面的函数优化公式, //对象的话,返回是否对象匹配的函数。 //返回一个property函数,这个函数调用传一个对象,value是键,返回值 varcb=function(value,context,argCount){ if(value==null)return_.identity; if(_.isFunction(value))returnoptimizeCb(value,context,argCount); if(_.isObject(value))return_.matcher(value); return_.property(value); }; _.identity=function(value){ returnvalue; }; _.property=function(key){ returnfunction(obj){ returnobj==null?void0:obj[key]; }; }; //源码中没有用到。返回一个迭代函数吧。 _.iteratee=function(value,context){ returncb(value,context,Infinity); }; //创建一个对象合并器.keyfunc是一个对象键数组函数,返回对象所有键。 //undefinedOnly么是定义是不是只有往目标对象上添加属性而不管属性是否存在。一般为 //true。也就是当对象不存在这个属性的时候才添加。 //当返回的函数只有一个参数时或者直接调用,直接返回那个参数。 //否则把对象的参数一个个遍历出来,添加到新对象,再返回新对象 //放个参考的 //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign varcreateAssigner=function(keysFunc,undefinedOnly){ returnfunction(obj){ varlength=arguments.length; if(length<2||obj==null)returnobj; for(varindex=1;index<length;index++){ varsource=arguments[index], keys=keysFunc(source), l=keys.length; for(vari=0;i<l;i++){ varkey=keys[i]; if(!undefinedOnly||obj[key]===void0)obj[key]=source[key]; } } returnobj; }; }; //创建一个对象,原型是参数prototype //参数不是对象的话,直接返回 //有原生的Object.create方法的话直接用 //利用Ctor构造函数创建对象原型。 varbaseCreate=function(prototype){ if(!_.isObject(prototype))return{}; if(nativeCreate)returnnativeCreate(prototype); Ctor.prototype=prototype; varresult=newCtor; Ctor.prototype=null; returnresult; }; //数组索引的最大值 varMAX_ARRAY_INDEX=Math.pow(2,53)-1; //是不是类数组,包括原生数组和nodelist类型 varisArrayLike=function(collection){ varlength=collection&&collection.length; returntypeoflength=='number'&&length>=0&&length<=MAX_ARRAY_INDEX; }; //前面一些基础函数写完了。写正式的了。首先集合方法。 //对数组和对象的键集合进行迭代遍历.每个key调用函数,返回原来的obj _.each=_.forEach=function(obj,iteratee,context){ //iteratee默认3个参数 iteratee=optimizeCb(iteratee,context); vari,length; //数组直接遍历,对象遍历键集合 if(isArrayLike(obj)){ for(i=0,length=obj.length;i<length;i++){ iteratee(obj[i],i,obj); } }else{ varkeys=_.keys(obj); for(i=0,length=keys.length;i<length;i++){ iteratee(obj[keys[i]],keys[i],obj); } } returnobj; }; //每个key调用函数,返回调用后集合数组 _.map=_.collect=function(obj,iteratee,context){ iteratee=cb(iteratee,context); //数组和对象的不同处理。 varkeys=!isArrayLike(obj)&&_.keys(obj), length=(keys||obj).length, results=Array(length); for(varindex=0;index<length;index++){ varcurrentKey=keys?keys[index]:index; results[index]=iteratee(obj[currentKey],currentKey,obj); } returnresults; }; //参数一大堆先不看了。黑 functioncreateReduce(dir){ functioniterator(obj,iteratee,memo,keys,index,length){ for(;index>=0&&index<length;index+=dir){ varcurrentKey=keys?keys[index]:index; memo=iteratee(memo,obj[currentKey],currentKey,obj); } returnmemo; } returnfunction(obj,iteratee,memo,context){ iteratee=optimizeCb(iteratee,context,4); varkeys=!isArrayLike(obj)&&_.keys(obj), length=(keys||obj).length, index=dir>0?0:length-1; if(arguments.length<3){ memo=obj[keys?keys[index]:index]; index+=dir; } returniterator(obj,iteratee,memo,keys,index,length); }; } _.reduce=_.foldl=_.inject=createReduce(1); _.reduceRight=_.foldr=createReduce(-1); //predicate为一个函数,遍历数组,若returntrue直接返回。里面用到一个_.findIndex函数,先研究下。研究在下面。 //_.findKey也差不多。最终函数返回obj[key]; _.find=_.detect=function(obj,predicate,context){ varkey; if(isArrayLike(obj)){ key=_.findIndex(obj,predicate,context); }else{ key=_.findKey(obj,predicate,context); } if(key!==void0&&key!==-1)returnobj[key]; }; //这个函数就是index搜索器,dir表示正者搜还是反着搜,搜到predicate函数返回的是ture的时候就把索引返回的,都没搜到就返回-1; functioncreateIndexFinder(dir){ returnfunction(array,predicate,context){ predicate=cb(predicate,context); varlength=array!=null&&array.length; varindex=dir>0?0:length-1; for(;index>=0&&index<length;index+=dir){ if(predicate(array[index],index,array))returnindex; } return-1; }; } _.findIndex=createIndexFinder(1); //和前面那个差不多,不过返回键名 _.findKey=function(obj,predicate,context){ predicate=cb(predicate,context); varkeys=_.keys(obj),key; for(vari=0,length=keys.length;i<length;i++){ key=keys[i]; if(predicate(obj[key],key,obj))returnkey; } }; //把predicate返回为true的堆到一个新数组,再返回这个新数组 _.filter=_.select=function(obj,predicate,context){ varresults=[]; predicate=cb(predicate,context); _.each(obj,function(value,index,list){ if(predicate(value,index,list))results.push(value); }); returnresults; }; //扩展自己,利用attrs类型的json,调用传入一个对象 _.matcher=_.matches=function(attrs){ attrs=_.extendOwn({},attrs); returnfunction(obj){ return_.isMatch(obj,attrs); }; }; //传对象和attrs,attrs类似{"a":1,"b":2}这种json的东西,若json类似的存在于obj中,则返回true,否则false; _.isMatch=function(object,attrs){ varkeys=_.keys(attrs),length=keys.length; if(object==null)return!length; varobj=Object(object); for(vari=0;i<length;i++){ varkey=keys[i]; if(attrs[key]!==obj[key]||!(keyinobj))returnfalse; } returntrue; }; //遍历obj中的每一个值,返回一个数组,这个数组包含properties所列出的属性的所有的键值对。 //这里的obj是类[{},{},{}]的东西 _.where=function(obj,attrs){ return_.filter(obj,_.matcher(attrs)); };
//全部obj的元素分别调用predicate,有一个返回值是false,整个返回false
//否则返回true
_.every=_.all=function(obj,predicate,context){
predicate=cb(predicate,context);
varkeys=!isArrayLike(obj)&&_.keys(obj),
length=(keys||obj).length;
for(varindex=0;index<length;index++){
varcurrentKey=keys?keys[index]:index;
if(!predicate(obj[currentKey],currentKey,obj))returnfalse;
}
returntrue;
};
//全部obj的元素分别调用predicate,有一个返回值是true,整个返回false
//否则返回false
_.some=_.any=function(obj,predicate,context){
predicate=cb(predicate,context);
varkeys=!isArrayLike(obj)&&_.keys(obj),
length=(keys||obj).length;
for(varindex=0;index<length;index++){
varcurrentKey=keys?keys[index]:index;
if(predicate(obj[currentKey],currentKey,obj))returntrue;
}
returnfalse;
};
//检查obj是否包含target,就是indexOf方法的调用。
_.contains=_.includes=_.include=function(obj,target,fromIndex){
if(!isArrayLike(obj))obj=_.values(obj);
return_.indexOf(obj,target,typeoffromIndex=='number'&&fromIndex)>=0;
};
//对每个obj元素调用method,注意method方法的获取,例子用了[]['sort'],获取到sort函数引用
//args,是传给method的参数,可选。
_.invoke=function(obj,method){
varargs=slice.call(arguments,2);
varisFunc=_.isFunction(method);
return_.map(obj,function(value){
varfunc=isFunc?method:value[method];
returnfunc==null?func:func.apply(value,args);
});
};
_.invoke([[5,1,7],[3,2,1]],'sort');
//传key,获得值组成的数组。
_.pluck=function(obj,key){
return_.map(obj,_.property(key));
};
varstooges=[{name:'moe',age:40},{name:'larry',age:50},{name:'curly',age:60}];
_.pluck(stooges,'name');
//结果["moe","larry","curly"]
//普通的数组且没有传iteratee的情况下,直接用交换机的方式比较最大值
//传了迭代函数的话,一般obj是[{a:1},{a:2},{a:3}],迭代函数把各个对象的相应键的值取出来比较。再返回最大值
_.max=function(obj,iteratee,context){
varresult=-Infinity,lastComputed=-Infinity,
value,computed;
if(iteratee==null&&obj!=null){
obj=isArrayLike(obj)?obj:_.values(obj);
for(vari=0,length=obj.length;i<length;i++){
value=obj[i];
if(value>result){
result=value;
}
}
}else{
iteratee=cb(iteratee,context);
_.each(obj,function(value,index,list){
computed=iteratee(value,index,list);
if(computed>lastComputed||computed===-Infinity&&result===-Infinity){
result=value;
lastComputed=computed;
}
});
}
returnresult;
};
//用Fisher-Yatesshuffle算法,产生随机排序数组
//https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
_.shuffle=function(obj){
varset=isArrayLike(obj)?obj:_.values(obj);
varlength=set.length;
varshuffled=Array(length);
for(varindex=0,rand;index<length;index++){
rand=_.random(0,index);
//这里用交换机,rand<index的时候,把shuffled【rand】索引的值放到shuffled【index】索引上
if(rand!==index)shuffled[index]=shuffled[rand];
shuffled[rand]=set[index];
}
returnshuffled;
};
//guard默认是1,n==null的时候,默认随机返回一个obj数组元素
//n有值的时候,返回n个元素
_.sample=function(obj,n,guard){
if(n==null||guard){
if(!isArrayLike(obj))obj=_.values(obj);
returnobj[_.random(obj.length-1)];
}
return_.shuffle(obj).slice(0,Math.max(0,n));
};
//一般用来转换nodelist为真正的数组,可以调用原生的数组方法。
_.toArray=function(obj){
if(!obj)return[];
if(_.isArray(obj))returnslice.call(obj);
if(isArrayLike(obj))return_.map(obj,_.identity);
return_.values(obj);
};
//数组的长度或者对象键集合的长度。
_.size=function(obj){
if(obj==null)return0;
returnisArrayLike(obj)?obj.length:_.keys(obj).length;
};
//obj每个元素分别调用predicate,返回true的扔到pass数组,false的扔到fail数组
_.partition=function(obj,predicate,context){
predicate=cb(predicate,context);
varpass=[],fail=[];
_.each(obj,function(value,key,obj){
(predicate(value,key,obj)?pass:fail).push(value);
});
return[pass,fail];
};
集合的函数差不多就这些,其实还有4个函数,觉得用不太到还挺难就不写了,哈哈哈哈哈
相关文章推荐
- Servlet程序开发--取得初始化配置信息
- Meteor发布、订阅
- 宁愿去陌生人那里,也不照顾熟人的生意,理由竟然是这样的?
- ApplicationContextUtil_me
- Mac 下android sudio 如何获取sha1与md5值
- SCSI即Small Computer System Interface小型计算机系统接口
- 栈和队列6|中缀表达式转换为后缀表达式 – 数据结构和算法28
- RHEL6.3安装ffmpeg
- Nginx 实战(一) 集群环境搭建 Nginx配置文件详细说明
- Activity和IntentFilter的匹配过程
- .net之OLEDB连接数据库
- Java 内存结构备忘录
- Android 之JDBC
- 什么是 A 轮融资?有 B轮 C轮么?
- setContentView(转)
- android生命周期参考
- sqlite学习网站:
- Redis-cluster集群【第三篇】:redis主从
- Proftp设置虚拟用户(转)
- 工作半年内容总结