zl程序教程

您现在的位置是:首页 >  后端

当前栏目

PrototypeEnumerable对象学习

2023-06-13 09:14:11 时间

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"

each方法:

调用这个方法时,其实就是对每个数据进行iterator操作,传入iterator函数的第一个参数为数据,第二个参数为索引,在遍历过程中可以抛出$continue和$break异常,注意:

Theusageof$continueisdeprecated.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]

eachSlice方法:

下面很多简单的方法就直接给出示例了,不在解释了,算法也没有什么难得,基本一看就懂:

复制代码代码如下:
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"]