PrototypeEnumerable对象学习
Enumerableprovidesalargesetofusefulmethodsforenumerations,thatis,objectsthatactascollectionsofvalues.ItisacornerstoneofPrototype.
Enumerableiswhatweliketocallamodule:aconsistentsetofmethodsintendednotforindependentuse,butformixin:incorporationintootherobjectsthat“fit”withit.
Quiteafewobjects,inPrototype,mixEnumerableinalready.ThemostvisiblecasesareArrayandHash,butyou"llfinditinlessobviousspotsaswell,suchasinObjectRangeandvariousDOM-orAJAX-relatedobjects.
上面这短话的意思大概就是说Enumerable是Prototype框架的基石,而Enumerable不单独使用,在Prototype中其它对象mix了Enumerable里面的方法,这样就可以在这些对象上应用Enumerable的方法,这样的对象有:Array,Hash,ObjectRange,还有一些和DOM,AJAX相关的对象。
个人理解Enumerable相当于C++中的抽象类的概念,其它类可以继承自这个类,并且实现Enumerable里面的抽象方法"_each",并且可以覆盖其它的方法,而Enumerable本身却不可以实例化,只可以实例化它的子类。
下面看一下如何通过Enumerable来写自己的类:
varYourObject=Class.create();
Object.extend(YourObject.prototype,Enumerable);Object.extend(YourObject.prototype,{
initialize:function(){
//withwhateverconstructorargumentsyouneed
//Yourconstructioncode
},
_each:function(iterator){
//Youriterationcode,invokingiteratorateveryturn
},
//Yourothermethodshere,includingEnumerableoverrides
});
可以看出最重要的就是实现_each方法,initialize方法就相当于构造函数,如果不需要外部传进来什么参数,完全可以省略。下面我自己写了一个产生随机数数组的类,非常简单,有许多不完善的地方,这里只做演示用:
//创建RandomArray类
varRandomArray=Class.create();
//mixinEnumerable
Object.extend(RandomArray.prototype,Enumerable);
//实现_each和所需方法
Object.extend(RandomArray.prototype,{
initialize:function(min,max,count){
this.min=min;
this.max=max;
this.count=count;
this._numbers=[];
this._createRandomArray();
},
_each:function(iterator){
varindex=this.count;
while(index-->0){
iterator(this._numbers[index]);
}
},
//产生随机数数组
_createRandomArray:function(){
varindex=0;
while(index<this.count){
varrandom=Math.round(Math.random()*(this.max-this.min)+this.min);
if(this.include(random)){
continue;
}
this._numbers[index++]=random;
}
},
include:function(number){
returnthis._numbers.indexOf(number)!=-1;
}
});
varobj=newRandomArray(4,19,5);
//alert(obj.size());
alert(obj.entries());
看一下Enumerable的源码,然后具体学习其中的每个方法:
var$break={};
varEnumerable=(function(){
//遍历每个数据
functioneach(iterator,context){
varindex=0;
try{
this._each(function(value){
iterator.call(context,value,index++);
});
}catch(e){
if(e!=$break)throwe;
}
returnthis;
}
//把数据划分成N组,其中每组有number个数,最后一组可能小于number个数
functioneachSlice(number,iterator,context){
varindex=-number,slices=[],array=this.toArray();
if(number<1)returnarray;
while((index+=number)<array.length)
slices.push(array.slice(index,index+number));
returnslices.collect(iterator,context);
}
//测试是否所有数据都满足某个条件
functionall(iterator,context){
iterator=iterator||Prototype.K;
varresult=true;
this.each(function(value,index){
result=result&&!!iterator.call(context,value,index);
if(!result)throw$break;
});
returnresult;
}
//检查是否有任意一个数据满足某个条件
functionany(iterator,context){
iterator=iterator||Prototype.K;
varresult=false;
this.each(function(value,index){
if(result=!!iterator.call(context,value,index))
throw$break;
});
returnresult;
}
//可以对所有数据进行任何操作,并返回结果数组
functioncollect(iterator,context){
iterator=iterator||Prototype.K;
varresults=[];
this.each(function(value,index){
results.push(iterator.call(context,value,index));
});
returnresults;
}
//查找第一个满足某个条件的数据,并返回,相当于find方法的别名
functiondetect(iterator,context){
varresult;
this.each(function(value,index){
if(iterator.call(context,value,index)){
result=value;
throw$break;
}
});
returnresult;
}
//查找所有满足某个条件的数据,并返回结果
functionfindAll(iterator,context){
varresults=[];
this.each(function(value,index){
if(iterator.call(context,value,index))
results.push(value);
});
returnresults;
}
//根据filter条件过滤所有数据,找到满足filter条件的数据,并返回结果
//filter为字符串或者正则表达式
functiongrep(filter,iterator,context){
iterator=iterator||Prototype.K;
varresults=[];
if(Object.isString(filter))
filter=newRegExp(RegExp.escape(filter));
this.each(function(value,index){
if(filter.match(value))
results.push(iterator.call(context,value,index));
});
returnresults;
}
//检查是否包含某个数据
functioninclude(object){
if(Object.isFunction(this.indexOf))
if(this.indexOf(object)!=-1)returntrue;
varfound=false;
this.each(function(value){
if(value==object){
found=true;
throw$break;
}
});
returnfound;
}
//和eachSlice方法类似,如果最后一组元素个数不足number,则用fillWith参数填充
functioninGroupsOf(number,fillWith){
fillWith=Object.isUndefined(fillWith)?null:fillWith;
returnthis.eachSlice(number,function(slice){
while(slice.length<number)slice.push(fillWith);
returnslice;
});
}
//对所有数据连续进行某个操作,可以实现累加或者累乘等操作
functioninject(memo,iterator,context){
this.each(function(value,index){
memo=iterator.call(context,memo,value,index);
});
returnmemo;
}
//在所有数据上执行某个方法
functioninvoke(method){
varargs=$A(arguments).slice(1);
returnthis.map(function(value){
returnvalue[method].apply(value,args);
});
}
//找数据中的最大值
functionmax(iterator,context){
iterator=iterator||Prototype.K;
varresult;
this.each(function(value,index){
value=iterator.call(context,value,index);
if(result==null||value>=result)
result=value;
});
returnresult;
}
//找数据中的最小值
functionmin(iterator,context){
iterator=iterator||Prototype.K;
varresult;
this.each(function(value,index){
value=iterator.call(context,value,index);
if(result==null||value<result)
result=value;
});
returnresult;
}
//把所有数据一分为二,第一组为满足某个条件的数据,第二组为不满足条件的数据
functionpartition(iterator,context){
iterator=iterator||Prototype.K;
vartrues=[],falses=[];
this.each(function(value,index){
(iterator.call(context,value,index)?
trues:falses).push(value);
});
return[trues,falses];
}
//取出所有数据的property的值,并返回结果
functionpluck(property){
varresults=[];
this.each(function(value){
results.push(value[property]);
});
returnresults;
}
//找到不满足某个条件的数据
functionreject(iterator,context){
varresults=[];
this.each(function(value,index){
if(!iterator.call(context,value,index))
results.push(value);
});
returnresults;
}
//根据某个条件对所有数据进行排序
functionsortBy(iterator,context){
returnthis.map(function(value,index){
return{
value:value,
criteria:iterator.call(context,value,index)
};
}).sort(function(left,right){
vara=left.criteria,b=right.criteria;
returna<b?-1:a>b?1:0;
}).pluck("value");
}
//返回数据的数组表示形式
functiontoArray(){
returnthis.map();
}
//基本就是把两组数据放在一起进行某些操作
functionzip(){
variterator=Prototype.K,args=$A(arguments);
if(Object.isFunction(args.last()))
iterator=args.pop();
varcollections=[this].concat(args).map($A);
returnthis.map(function(value,index){
returniterator(collections.pluck(index));
});
}
functionsize(){
returnthis.toArray().length;
}
//返回表示Enumerable对象的字符串表示形式
functioninspect(){
return"#<Enumerable:"+this.toArray().inspect()+">";
}
return{
each:each,
eachSlice:eachSlice,
all:all,
every:all,
any:any,
some:any,
collect:collect,
map:collect,
detect:detect,
findAll:findAll,
select:findAll,
filter:findAll,
grep:grep,
include:include,
member:include,
inGroupsOf:inGroupsOf,
inject:inject,
invoke:invoke,
max:max,
min:min,
partition:partition,
pluck:pluck,
reject:reject,
sortBy:sortBy,
toArray:toArray,
entries:toArray,
zip:zip,
size:size,
inspect:inspect,
find:detect
};
})();
下面学习Enumerable所提供的方法:
all
any
collect
detect
each
eachSlice
entries
find
findAll
grep
inGroupsOf
include
inject
invoke
map
max
member
min
partition
pluck
reject
select
size
sortBy
toArray
zip
all方法:
Determineswhetheralltheelementsareboolean-equivalenttotrue,eitherdirectlyorthroughcomputationbytheprovidediterator.
基本就是调用each方法,检查每个数据是否满足iterator条件,其中有一个不满足就抛出$break异常,然后在each方法里面会捕获这个异常。这里注意一下"!!"的用法,可以把某些对象转换成相应的bool值:
!!{} true
!![] true
!!"" false
!!"string" true
!!0 false
下面看一下示例:
[].all()
//->true(emptyarrayshavenoelementsthatcouldbefalse-equivalent)
$R(1,5).all()
//->true(allvaluesin[1..5]aretrue-equivalent)
[0,1,2].all()
//->false(withonlyoneloopcycle:0isfalse-equivalent)
[9,10,15].all(function(n){returnn>=10;})
//->false(theiteratorwillreturnfalseon9)
$H({name:"John",age:29,oops:false}).all(function(pair){returnpair.value;})
//->false(theoops/falsepairyieldsavalueoffalse)
any方法:
跟all方法差不多,就不详细说了,看一下示例
[].any()
//->false(emptyarrayshavenoelementsthatcouldbetrue-equivalent)
$R(0,2).any()
//->true(onthesecondloopcycle,1istrue-equivalent)
[2,4,6,8,10].any(function(n){return0==n%3;})
//->true(theiteratorwillreturntrueon6:thearraydoeshave1+multipleof3)
$H({opt1:null,opt2:false,opt3:"",opt4:"pfew!"}).any(function(pair){returnpair.value;})
//->true(thankstotheopt4/"pfew!"pair,whosevalueistrue-equivalent)
collect/map(collect方法别名)方法:
Returnstheresultsofapplyingtheiteratortoeachelement.Aliasedasmap.
ThisisasortofSwiss-Armyknifeforsequences.Youcanturntheoriginalvaluesintovirtuallyanything!
这个方法被称为”瑞士军刀“,这个方法基本上可以对数据进行任何操作,其中map方法是这个方法的别名,这个方法实现很简单,results.push(iterator.call(context,value,index));这句话是关键,就是对每个数据进行iterator调用,并且把结果存储到要返回的数组中,看一下示例:
["Hitch","Hiker"s","Guide","To","The","Galaxy"].collect(function(s){returns.charAt(0).toUpperCase();}).join("")
//->"HHGTTG"
$R(1,5).collect(function(n){returnn*n;})
//->[1,4,9,16,25]
注意帮助文档上最后有这样几行提示:
First,themethod-callingscenario:youwanttoinvokethesamemethodonallelements,possiblywitharguments,andusetheresultvalues.Thiscanbeachievedeasilywithinvoke.
Second,theproperty-fetchingscenario:youwanttofetchthesamepropertyonallelements,andusethose.Thisisabreezewithpluck.
detect方法:
找到第一个满足某个条件的数据,并返回,看一下示例,其实这个方法就是find的别名,所以调用detect和find是一样的:
//Anoptimalexactprimedetectionmethod,slightlycompacted.
functionisPrime(n){
if(2>n)returnfalse;
if(0==n%2)return(2==n);
for(varindex=3;n/index>index;index+=2)
if(0==n%index)returnfalse;
returntrue;
}
//isPrime
$R(10,15).find(isPrime)//->11
["hello","world","this","is","nice"].find(function(s){returns.length<=3;})
//->"is"
调用这个方法时,其实就是对每个数据进行iterator操作,传入iterator函数的第一个参数为数据,第二个参数为索引,在遍历过程中可以抛出$continue和$break异常,注意:
Theusageof
isdeprecated.ThisfeaturewillnotbeavailableinreleasesafterPrototype1.5infavorofspeed.
看一下示例:
["one","two","three"].each(function(s){alert(s);});
["hello","world"].each(function(s,index){alert(index+":"+s);});
//alerts->"0:hello"then"1:world"
//Thiscouldbedonebetterwithanaccumulatorusinginject,buthumorme
//here...
varresult=[];
$R(1,10).each(function(n){
if(0==n%2)throw$continue;
if(n>6)throw$break;
result.push(n);
});
//result->[1,3,5]
下面很多简单的方法就直接给出示例了,不在解释了,算法也没有什么难得,基本一看就懂:
varstudents=[{name:"Sunny",age:20},{name:"Audrey",age:21},{name:"Matt",age:20},{name:"Élodie",age:26},{name:"Will",age:21},{name:"David",age:23},{name:"Julien",age:22},{name:"Thomas",age:21},{name:"Serpil",age:22}];
students.eachSlice(4,function(toon){returntoon.pluck("name");})
//->[["Sunny","Audrey","Matt","Élodie"],
//["Will","David","Julien","Thomas"],
//["Serpil"]]
//下面的first方法是Array对象提供的
students.eachSlice(2).first()
//->[{name:"Sunny",age:20},{name:"Audrey",age:21}]
entries方法,就是toArray方法,toArray方法里面又调用map方法,map方法就相当于collect方法:
$R(1,5).toArray()//->[1,2,3,4,5]
find/findAll(select方法的别名)方法:
//find方法
["hello","world","this","is","nice"].find(function(s){returns.length<=3;})
//->"is"
//findAll方法
$R(1,10).findAll(function(n){return0==n%2;})
//->[2,4,6,8,10]["hello","world","this","is","nice"].findAll(function(s){returns.length>=5;})
//->["hello","world"]
grep方法:
这个方法需要注意的就是,参数filter在函数里面是要被统一成正则表达式的,然后调用正则表达式的match方法来进行判断
//Getallstringswitharepeatedlettersomewhere
["hello","world","this","is","cool"].grep(/(.)\1/)
//->["hello","cool"]
//Getallnumbersendingwith0or5
$R(1,30).grep(/[05]$/)
//->[5,10,15,20,25,30]
//Those,minus1
$R(1,30).grep(/[05]$/,function(n){returnn-1;})
//->[4,9,14,19,24,29]
//Getallstringswitharepeatedlettersomewhere
["hello","world","this","is","cool"].grep(/(.)\1/)
//->["hello","cool"]
//Getallnumbersendingwith0or5
$R(1,30).grep(/[05]$/)
//->[5,10,15,20,25,30]
//Those,minus1
$R(1,30).grep(/[05]$/,function(n){returnn-1;})
//->[4,9,14,19,24,29]
inGroupsOf方法:
varstudents=[{name:"Sunny",age:20},{name:"Audrey",age:21},{name:"Matt",age:20},{name:"Élodie",age:26},{name:"Will",age:21},{name:"David",age:23},{name:"Julien",age:22},{name:"Thomas",age:21},{name:"Serpil",age:22}];
//pluck方法就是取得对象的某个属性,这里取得的是name属性
students.pluck("name").inGroupsOf(4)
//->[["Sunny","Audrey","Matt","Élodie"],
//["Will","David","Julien","Thomas"],
//["Serpil",null,null,null]]
include/member(include方法的别名)方法,这里先检查了一下对象上是否有indexOf方法,如果有的话则直接调用这个方法:
$R(1,15).include(10)
//->true
["hello","world"].include("HELLO")
//->false
[1,2,"3","4","5"].include(3)
//->true(==ignoresactualtype)
inject方法:
$R(1,10).inject(0,function(acc,n){returnacc+n;})
//->55(sumof1to10)
$R(2,5).inject(1,function(acc,n){returnacc*n;})
//->120(factorial5)
["hello","world","this","is","nice"].inject([],function(array,value,index){
if(0==index%2)array.push(value);
returnarray;
})
//->["hello","this","nice"]
invoke方法:
["hello","world","cool!"].invoke("toUpperCase")
//["HELLO","WORLD","COOL!"]
["hello","world","cool!"].invoke("substring",0,3)
//["hel","wor","coo"]
max/min方法:
$R(1,10).max()//->10
["hello","world","gizmo"].max()
//->"world"
functionPerson(name,age){this.name=name;this.age=age;}
varjohn=newPerson("John",20);
varmark=newPerson("Mark",35);
vardaisy=newPerson("Daisy",22);
[john,mark,daisy].max(function(person){returnperson.age;})
//->35
partition方法:
["hello",null,42,false,true,,17].partition()
//->[["hello",42,true,17],[null,false,undefined]]
$R(1,10).partition(function(n){return0==n%2;})
//->[[2,4,6,8,10],[1,3,5,7,9]]
["hello",null,42,false,true,,17].partition()
//->[["hello",42,true,17],[null,false,undefined]]
$R(1,10).partition(function(n){return0==n%2;})
//->[[2,4,6,8,10],[1,3,5,7,9]]
pluck方法:
["hello","world","this","is","nice"].pluck("length")
//->[5,5,4,3,4]
reject方法:
$R(1,10).reject(function(n){return0==n%2;})
//->[1,3,5,7,9]
["hello","world","this","is","nice"].reject(function(s){returns.length>=5;})
//->["this","is","nice"]
$R(1,10).reject(function(n){return0==n%2;})
//->[1,3,5,7,9]
["hello","world","this","is","nice"].reject(function(s){returns.length>=5;})
//->["this","is","nice"]
size方法省略。
sortBy方法:
这个方法首先通过map方法返回一个对象数组,然后调用数组的sort方法,最后在取出对象中的value属性
["hello","world","this","is","nice"].sortBy(function(s){returns.length;})
//->"is","this","nice","hello","world"]
["hello","world","this","is","cool"].sortBy(function(s){
varmd=s.match(/[aeiouy]/g);
returnnull==md?0:md.length;
})
//->["world","this","is","hello","cool"](sortedbyvowelcount)
zip方法:
varfirstNames=["Justin","Mislav","Tobie","Christophe"];
varlastNames=["Palmer","Marohnić","Langel","Porteneuve"];
firstNames.zip(lastNames)
//->[["Justin","Palmer"],["Mislav","Marohnić"],["Tobie","Langel"],["Christophe","Porteneuve"]]
//通过这个例子我们可以看出参数a就是一个数组,表示两个数组中的相对应的项
firstNames.zip(lastNames,function(a){returna.join("");})
//->["JustinPalmer","MislavMarohnić","TobieLangel","ChristophePorteneuve"]
//通过这个例子我们可以看到传入的可以是多个数组
varcities=["Memphis","Zagreb","Montreal","Paris"];
firstNames.zip(lastNames,cities,function(p){returnp[0]+""+p[1]+","+p[2];})
//->["JustinPalmer,Memphis","MislavMarohnić,Zagreb","TobieLangel,Montreal","ChristophePorteneuve,Paris"]
firstNames.zip($R(1,100),function(a){returna.reverse().join(".");})
//->["1.Justin","2.Mislav","3.Tobie","4.Christophe"]
相关文章
- C++学习——类和对象
- Flask 学习-5.请求对象Request
- Flask 学习-36.Flask-RESTful 序列化输出对象
- 字符串常量池 运行时常量池_常量池中的字符串是对象吗
- React源码学习入门(四)深入探究React中的对象池
- js中map遍历数组对象_js遍历数组
- 灵活的令人抓狂,如何在运行时修改某一个 Python 对象的类?
- yhd-ExcelVBA学习Collection对象
- arraylist遍历 并删除_js遍历list对象
- 8. Groovy 运算符 条件运算符,对象运算符学习
- Java的学习笔记(11)对象 六
- Java的学习笔记(14)对象 九
- JavaWeb学习总结(十七)——JSP中的九个内置对象详解编程语言
- javaweb学习总结(八)——HttpServletResponse对象(二)详解编程语言
- [javaSE] 练习队列线程和对象序列化详解编程语言
- Hibernate evict方法:从session缓存中清除对象
- MySQL: 简单实现对象存储(mysql对象存储)
- PrototypeClass对象学习
- JavaScript学习笔记(二)js对象
- javascript学习笔记(八)js内置对象
- javascript学习笔记(十四)window对象使用介绍
- js利用image对象实现图片的预加载提高访问速度
- JS对象与json字符串格式转换实例
- Javascript学习笔记之函数篇(四):arguments对象
- JavaScript学习笔记之JS事件对象
- JavaScript学习笔记之Cookie对象
- Python深入学习之对象的属性