数组:Array

来源:互联网 发布:淘宝信用卡有什么好处 编辑:程序博客网 时间:2024/04/27 22:25
 

第12章 数组:Array

本章导读

对于初学者而言,本章难度不大,应尽量全部掌握。12.2.7节的内容稍微有些难度,如果不明白,可以暂时不看,待日后碰到类似问题时,再来查阅。

对ActionScript 2用户而言,阅读本章时,要留心阅读ActionScript 3中数组新增的功能,见12.2.1节、12.2.7节及12.3节。

对于与Java、C#等其他语言用户,Array在ActionScript 3中的实现与使用,有诸多不同,本章应仔细阅读。12.1节中介绍了ActionScript 3中数组的内容和概念。

数组(Array)是最重要的数据结构之一。将Array所有功能熟练掌握,是成为优秀ActionScript 3程序员的第一步,也对日常编程大有裨益。而且,Action Script3中大幅度地改进了对Array的支持,新增加了近十种常用的操作。

ActionScript 3操作数组得到前所未有的方便。

12.1 数组(Array)的介绍

在2.4.5节中,我们对数组做过详细的解释。如果不清楚,请往前参看2.4.5节。

与其他语言对比

对Java、C#用户要强调的是,ActionScript 3中的数组(Array)是以非负整数为索引的稀疏数组(sparse array)。不要求所有元素统一类型,不支持类型化数组(typed array)。

12.1.1 访问数组元素

我们使用索引访问数组中的元素。数组的第一元素的索引值是0,第二个是1,以此类推,访问方式如下面代码所示:

//新建一个数组var foo:Array = ["one", "two ", "three"];//使用索引访问第二个元素。数组索引从0 开始,所以第二个元素索引为1trace(foo[1]);//输出:two

12.1.2 数组的长度

ActionScript 3中的数组的长度(length)不是创建好就固定不变的,数组的长度可以随着成员的增删而变化。

进阶知识

事实上,数组的length的读取是通过getter方法实现的。而且,length的设置也是setter方法。如果设定的length小于现有数组长度,那么索引大于[原来数组长度-1]的元素将被统统丢掉。如果设置了一个数组的length大于现有的长度,那么从原数组最后一个元素到新数组末尾中间空出的元素将全部自动设为undefined。

我们可以通过length属性来访问数组的长度。

实现的方式如下,注意length是uint型,即非负整数。

public function get length():uint public function set length(value:uint)

示例12-1将示范length的常用用法。下例演示了3种方法:正常访问length得到数组长度;使用超过"数组长度-1"的索引值来给一个新的数组成员赋值,导致数组发生变化;整行设置数组长度小于当前长度,导致数组删减。

示例12-1数组长度(length)的常用用法

var foo:Array = ["one", "two", "three"];trace(foo.length);//输出:3//设定foo的长度为5foo.length = 5;trace(foo);//输出:one,two,three,,//可以看到,多出了两个空的元素//使用超过length的索引来设定元素foo[7] = "out ranged";trace(foo);//输出:one,two,three,,,,,,,out rangedtrace(foo.length);//输出:8//强行设定foo的length 为3foo.length = 3;trace(foo);//输出:one,two,three//结果foo将3个元素以后的所有元素全部扔掉了

12.1.3 多维数组

多维数组本质和数组没有不同。不同之处在于,多维数组元素仍然是数组。

关于多维数组的操作,在2.4.7节"多维数组"中讲得比较详细了,此处不再赘述。

12.2 数组(Array)的基本操作

数组的操作比较多。在很多情况下,数组创建好后,不会是一成不变的,根据不同的应用,会增加、减少或者插入数组元素等。

举个例子,如果我们创建好了一个数组,里面按顺序放好了一套书的名字,形成了一套书单。

var booklist:Array = ["ActionScript 3殿堂之路","Flex3殿堂之路","AIR殿堂之路"];

那么一般会有哪些改变书单的需求呢?会有4类。

● 第一类:查找元素。例如,就是查找书名。

● 第二类:增删数组元素。例如,修改书单,包括删除书、添加书。

● 第三类:数组排序。例如,对书单进行各种排序。

● 第四类:抽取数组部分元素生成新数组。例如,从现有书单中再选择一部分书,生成新书单。

我们虽然可以自己编写代码实现这些方法,但是没有必要,ActionScript 3已经贴心地给我们提供了现成的工具。下面我们来一个个说一下这些方法。

与ActionScript 2、ActionScipt1对比

ActionScript 3比ActionScript 2新增加了不少对数组的操作,在ActionScript 3中使用数组更加便利:indexOf、lastIndexOf、every、filter、forEach、map、some。

12.2.1 查找:indexOf、lastIndexOf

indexOf和lastIndexOf用来直接查询目标元素在数组中的索引。结合上面找书的例子,当知道一本书的书名,想知道该书在书单中处于第几个位置时,就可以用indexOf或lastIndexOf查询。

使用格式如下:

目标数组.indexof(要查找的元素)目标数组.lastIndexOf(要查找的元素)

indexOf和lastIndexOf的工作方式是indexOf用于从左到右查找,lastIn dexOf用于从右到左查。如果查得到就返回索引值,查不到就返回-1。换句标准的语言就是:indexOf按照索引值从小到大查找,一旦找到就立刻返回索引值;lastIndexOf按照索引值从大到小查找,一旦找到就立刻返回索引值,如果没有找到符合要求的元素,则返回整数值-1。

除了上面的用法外,这两个方法还可以定义查找的起始位置。indexOf从起始位置开始向数组尾部查询,lastIndexOf则是从起始位置向数组头部查询。

目标数组.indexOf(要查找的元素,查找的起始位置)目标数组.lastIndexOf(要查找的元素,查找的起始位置)

下面用示例12-2来演示。

示例12-2数组indexOf和lastIndexOf的用法

var foo:Array = ["apple", "banana", "pear", "berry", "apple", "banana"];trace("按索引从小到大查:"+foo.indexOf("banana"));trace("按索引从大到小查:"+foo.lastIndexOf("banana"));trace("从第三个元素开始,按索引从小到大查:"+foo.indexOf("banana", 2));trace("从第五个元素开始,按索引从大到小查:"+foo.lastIndexOf("banana", 4));/*输出:按索引从小到大查:1按索引从大到小查:5从第三个元素开始,按索引从小到大查:5从第五个元素开始,按索引从大到小查:1*/

按索引从小到大查:1

按索引从大到小查:5

从第三个元素开始,按索引从小到大查:5

从第五个元素开始,按索引从大到小查:1

进阶知识

判断查找的满足条件是,待查找的元素与数组中的元素全等,即使用"==="来判断是否全等,不会执行类型转换,这也就意味着字符串类型的"123"和数值类型的123不符合查找满足条件。

请看如下代码。

var target:String = "123";var number_ary:Array = [102, 123, 134];trace(target == number_ary[1]);//第二个元素是数值类型,target 是字符串类型,但是输出为:true//这表明target和number_ary的第二个元素相等,此处执行了类型转换trace(number_ary.indexOf(target));//输出:-1.表明number_ary 中没有和target相符的元素

12.2.2 在数组首尾新增元素:pushi、unshift

● push方法:在数组尾部新增一个或多个元素。

● unshift方法:在数组头部新增一个或多个元素。

进阶知识

push和unshift方法都有返回值。返回值是增加元素后的数组长度。

常用用法如下。

数组.push(元素);

数组.push(元素1,元素2,元素3,…元素n);

数组.unshift(元素);

数组.unshift(元素1,元素2,元素3,…元素n);

还以书单为例来解释:当要在这套书单后面新增两三本书时,需要在数组后面接上这新出的两三本书的名字,这时要用push():突然又要求在这套书最前面加两三本书时,用unshift()。

push()和unshift()一次可以添加多个元素,元素之间用逗号隔开。具体使用方法见示例12-3。

var bookList:Array = ["Book I", "Book II", "Book III"];trace(bookList.push("A"));//数组尾部加入A,并用trace输出push方法的返回值trace(bookList.push("B", "C", "D"));//可以同时加入多个元素trace(bookList);/*输出47Book I,Book II,Book III,A,B,C,D*/trace(bookList.unshift("1"));//数组头部加入1,并用输出unshift方法的返回值trace(bookList.unshift("2", "3", "4"));//可以同时加入多个元素trace(bookList);/*输出8112,3,4,1,Book I,Book II,Book III,A,B,C,D*/

12.2.3 删除数组首尾元素:pop、shift

● pop方法用于将数组最后一个元素删除。

● shift方法用于将数组第一个元素删除,剩余元素索引值自动减1。

pop和shift不需要参数,使用格式为:

数组.pop();数组.shift();

进阶知识

pop和shift都有返回值。返回值就是那个被删除的元素。

pop和shift每使用一次只删除一个元素,见示例12-4。如果需要一次删除

多个元素,请使用splice方法,将在下一节中介绍。

示例12-4数组方法pop和shift的使用

var bookList:Array = ["Book I", "Book II", "Book III"];//下句执行了pop,并输出被删除的元素trace("被pop 删掉的书是:"+bookList.pop());//下句执行了shift,并输出被删除的元素trace("被shift删掉的书是:"+bookList.shift());trace("原书单变成:"+bookList);/*被pop删掉的书是:Book III被shift 删掉的书是:Book I原书单变成:Book II*/

12.2.4 在数组当中插入或者删除元素:灵活多变的splice

Splice方法可以删除数组当中的一个或多个连续的元素。splice用法灵活,用途广泛。它的各种不同用法都有其专门的用途,请读者留意。

一般有以下4种用途。

(1)从数组某位置开始,删除指定数目的元素,并插入一些新元素。这是splice的标准用法。用法格式如下。

数组.splice(删除点的索引,要删除的元素数目,新元素1,新元素2,…新元素n);

删除点的索引可以一负值,表示数组从尾部倒数的位置。比如,索引为-3,指的是数组倒数第三个元素,素引为-1,则是倒数第一个元素。

(2)删除数组中某位置之后几个元素。

数组.splice(删除点的索引,要删除的元素数目);

(3)删除数组中某位置之后所有元素。

数组.splice(删除点索引);

(4)在数组中某位置之后,插入新元素。

数组.splice(删除点索引,0,新元素1,新元素2,…新元素n);

进阶知识

splice有返回值,返回的是一个数组,包含的就是删除的那些元素。

另外要记住,splice改变了原数组的内容。如果只是想得到数组中的一段内容而不想改变原数组,那么应该使用slice方法。Slice方法不改变原数组内容。

下面的代码分别展示了这4种用法,请见示例12-5。

示例12-5数组splice方法的4种使用方法

用法1;

//从索引为2 的元素开始删除两个元素,并插入3 个新元素//并用trace 输出splice删除的元素trace(bookList.splice(2, 2, "NewBook1", "NewBook2", "NewBook3"));trace(bookList);//输出splice改变后的原数组/*输出:Book III,Book IVBook I,Book II,NewBook1,NewBook2,NewBook3,Book V*/

用法2;

//从索引为1 的元素开始删除两个元素//并用trace 输出splice删除的元素trace(bookList.splice(1, 2));trace(bookList);/*输出:Book II,NewBook1Book I,NewBook2,NewBook3,Book V*/

用法3;

//从索引为3 的元素开始删除所有元素//并用trace 输出splice删除的元素trace(bookList.splice(3));trace(bookList);/*输出:Book VBook I,NewBook2,NewBook3*/

用法4;

//在索引2 的元素后面插入两新元素//并用trace 输出splice 删除的元素。由于此处没有删除任何元素,所以输出为空trace(bookList.splice(2, 0, "书1", "书2"));trace(bookList);/*输出:Book I,NewBook2,书1,书2,NewBook3*/

slice可以获取数组中一段连续的元素,而不改变原有数组内容。它的使用很方便,有两个参数:一个是截取范围的起点索引,另一个是截取范围的终点索引。slice的起点和终点索引值都可以是负值。负值就是倒数的索引,-1就是最后一个元素,-2是倒数第二个,以此类推。

返回值是截取的元素组成的新数组,而且不改变原有数组的内容。新数组中包括起点索引的元素,而不包括终点索引的元素。

Slice的用法如下。

数组.slice(起点索引,终点索引);

进阶知识

如果输入时,不慎将输入的起点索引设置为大于终点索引值,那么slice只会返回一个空数组。当起点、终点中有一个是负值时,容易产生这种误操作。

如果只传一个参数给slice,那么将把这个参数当成起始索引值,截取从这个索引值开始,到数组终点这一段元素,生成新数组。用法如下。

数组.slice(起点索引)

当不传任何参数给slice方法时,将生成原数组的一个浅复制。关于这一点,请参见12.2.7节"*数组的浅复制与深复制"。

slice和splice的区别:

● slice不改变原有的数组内容,splice改变。

● slice根据指定起点和终点确定截取范围,splice根据指定起点和截取数量确定截取范围。

示例12-6演示了slice的用法,包括起点索引大于终点索引时的异常情况、使用负数索引、使用单参数的slice等。

示例12-6数组slice方法的使用

var foo:Array = ["A", "B", "C", "D", "E", "F"];trace(foo.slice(2, 4));//输出:C,Dtrace(foo.slice(4, 2));//输出为空,因为起点索引大于终点索引了,slice返回的是一个空数组trace(foo.slice(-3, -1));//输出:D,Etrace(foo.slice(3));//输出:D,E,Ftrace(foo.slice(-2));//输出:E,Ftrace(foo);//输出:A,B,C,D,E,F

12.2.6 将多个元素或数组拼合成新的数组:concat

concat可将多个元素或数组拼成一个新的数组,并通过返回值返回,不改变原有数组内容:用法格式如下。参数可以是一个也可以是多个。

但不传任何参数给concat方法时,将生成原数组的一个浅复制。关于这一点,请详细参考"*数组的浅复制与深复制"。

数组.concat(参数1,参数,参数3…参数n)

concat和push的相同之处在于,都是将参数中的元素按顺序加到数组的后面。

concat和push的不同点是:

● concat不改变原有数组内容,而是生成一个新数组返回。push直接改变原数组内容。

● 当参数是数组时,concat和push的行为不同。concat会将数组分开成各个元素按顺序加入,而push则把这个是数组的参数当成一个元素加入原数组后面。

示例12-7分别用concat和push把数组foo和3个元素连接在一块儿,比较用法和区别。注意注释,体会concat的用法。

示例12-7数组concat方法的用法

var foo:Array = ["A", "B", "C"];var bar1:int=1;var bar2:Array = [2, 3, 4];var bar3:Array = ["a", "b", "c"];var newFoo = foo.concat(bar1, bar2, bar3);trace(newFoo);//输出:A,B,C,1,2,3,4,a,b,ctrace(newFoo.length);//输出:10//数组长度为10,说明确实是将数组bar2,bar3 拆开成元素再添加的trace(foo);//输出:A,B,C// foo的内容不变,证明concat 确实没有改变foo的内容foo.push(bar1,bar2,bar3);trace(foo);//输出:A,B,C,1,2,3,4,a,b,c// foo的内容改变了。表面上看起foo 的内容和newFoo 一样,但请看foo数组和度trace(foo.length);//输出:6//数组length只有6,说明push是把bar2,bar3各自当成一个元素加入的trace(foo[4]);//输出:2,3,4//输出foo数组的第五个元素,果然是一个数组

12.2.7 *数组的浅复制与深复制

复制一个数组,是指生成一个新的数组,内容和原有数组一样。数组复制经常需要用到,比较重要。

有两种复制的方法:浅复制和深复制。这个概念不是数组特有的。对引用类型的数据复制方式都有浅复制和深复制的区别。本节只讲述数组的浅复制和深复制。

1.何时用浅复制?何时用深复制?

当数组元素全部是基元数据类型(primitive data type)时,即全部是值类型时,复制是没有浅复制和深复制的分别的。若需要复制,使用下文中的浅复制方法即可。

但是当数组元素全是复杂数据类型(complex data type),即引用类型时,那么复制就会有两种选择:一种是浅复制,一种是深复制。关于值类型和引用类型的区别,请参阅2.2.4节"*重要:值类型和引用类型的区别"。

当数组部分元素是基元数据,部分元素是复杂类型数据时,也要考虑该采取哪种复制方式:由于ActionScript 3数组不是类型化数组,允许存储不同类型的数据,因此这种情况也是很常见的。对于基元数据类型的元素,不论用哪种方式复制,执行效果都一样。只有对于复杂数据类型的元素,才有深复制和浅复制的区别。

比如,数组A[1,"string",[1,2,3]],其第三个元素是数组,属于复杂类型元素。则需要考虑两种复制方式的影响。其余两个元素,一个是数值型,一个是字符串型,都属于基元数据类型,不需要考虑采用何种复制方式。

2.浅复制

生成数组浅复制的方法很简单,只需要调用原数组的slice方法或者concat方法,不传任何参数,即可生成原数组的浅复制。用法示例如下。

var copy:Array = originalArray.concat();var copy:Array = originalArray.slice();

使用浅复制生成的新数组,其各个元素存放的只是引用。两个数组中相同位置的元素存放的引用指向同一个对象。那么,如果对该元素操作,等于就是操作引用指向的对象。如果该对象状态发生变化,那么也会影响到另一个数组中相应位置的元素。这一点需要注意。

示例12-8生动详细地展示了浅复制方法,以及运用浅复制必须注意的要点。先解释一下代码的内容。

有一个数组foo,其第一个元素是String型,属于值类型。它的第二个元素是Array类型,第三个元素是Object型,都属于引用类型。使用slice方法(或concat方法)生成数组foo的浅复制——数组bar。然后改动数组bar的元素内容,再和数组foo的元素一一比较。结果发现,改动数组bar中的值类型元素不会影响foo中的相应元素。但改动bar中的引用类型元素,则foo中相对应的元素内容也改变了。不仅新手,老手也常常忘了这一点,从而小吃苦头。

示例12-8数组的浅复制:slice和concat

var objectElement:Object = {name:"kingda", web:"www.kingda.org"};var arrayElement:Array = [1, 2, 3];var foo:Array = ["a string element", arrayElement, objectElement];trace(foo);//输出foo的内容:a string element,1,2,3,[object Object]//1.使用slice 方法生成foo的浅复制,赋值给barvar bar:Array = foo.slice();//也可以用var bar:Array = foo.concat();//2.比较foo 和bartrace(bar);//输出浅复制bar的内容:a string element,1,2,3,[object Object]//果然和foo完全一样trace(foo == bar);//输出false/*结果为false很正确因为对Array这个引用类型,"=="判断的是变量持有对象的引用是否相同,而不是内容相同。由于bar是slice方法(或者concat t方法)返回的一个新数组,因此肯定不和foo 变量原有的数组引用相同*/trace(foo[0] == bar[0]);//输出true//注意,第一个元素是值类型,因此"=="是根据值(value)来判断的trace(foo[1] == bar[1]);trace(foo[2] == bar[2]);/*输出:truetrue这两个元素是引用类型,"=="是根据引用是否相同来判断的*///3下面我们来挨个改变bar 数组所有元素,再来一一比较foo和barbar[0] = "a new string in bar";bar[1][0] = 1000;bar[2].web = "www.actionscript3.cn";trace(foo);//输出:a string element,1,2,3,[object Object]//注意,foo中的内容除了第一个元素,其余也跟着bar变了。改变了bar,同时也改变了foo//引用类型元素的内容trace(bar);//输出:a new string in bar,1000,2,3,[object Object]//可以看到,bar除了第一个元素和现在的foo不同外,其余都有一样//怎么知道第三个元素也改变了呢?我们来trace 一下它的web 属性trace(foo[2].web);//输出:www.actionscript3.cn//果然foo中的第三个元素的web属性也变了trace(bar[2].web);//输出:www.actionscript3.cn

3.深复制

使用深复制生成的新数组,其所有元素都是真正的原数组元素的备份。这时两个数组中相同位置的元素,存放的是不同的引用,指向不同的对象。但是这两个对象的状态是完全一致的。

如果生成深复制?这就需要使用到强大的ByteArray类了。方法见示例12-9。

示例12-9数组的深复制:ByteArray的运用

var objectElement:Object = {name:"kingda",web:"www.kingda.org"};var arrayElement:Array = [1,2,3];var foo:Array = ["a string element",arrayElement,objectElement];//以下4 行深复制数组foovar fooBA:ByteArray = new ByteArray();fooBA.writeObject(foo);fooBA.position = 0;var bar:Array = fooBA.readObject() as Array;trace(bar);//输出:a string element,1,2,3,[object Object]trace(foo == bar);//输出:false//表明foo和bar 持有的是两个不同对象的引用trace(foo[0] == bar[0]);//输出:true//由于第一个元素是String 型,因此"=="判断值相等,返回真trace(foo[1] == bar[1]);//输出:false//第二个元素是Array型,因此"=="判断引用是否相同,若不同,返回假trace(foo[2] == bar[2]);//输出:false//第三个元素是Object 型,因此"=="判断引用是否相同,若不同,返回假//以下3 行改变bar的元素bar[0] = "a new string in bar";bar[1][0] = 1000;bar[2].web = "www.actionscript3.cn";//再看foo是否受影响trace(foo);//输出:a string element,1,2,3,[object Object]trace(bar);//输出:a new string in bar,1000,2,3,[object Object]trace(foo[2].web);//输出:www.kingda.orgtrace(bar[2].web);//输出:www.actionscript3.cn//可以看出,bar的改变并没有影响foo。确确实实把所有的元素都真正复制了

12.2.8 排序:revese、sort、sortOn

数组排序也是经常用到的操作。比如说,播放的MP3歌单、调整任务序列等。

Actionscript 3对数组的排序工具比Actionscript 2更加丰富和强大。共有3种方法:revese、sort、sortOn。这3种排序方法都能改变原数组中元素的位置排列。

Revese方法能将数组元素倒序,返回值是新的数组。使用格式如下:

数组.reverse();

举例见示例12-10。

示例12-10使用reverse()的例子

var ary:Array = [“a”,”b”,”c”];trace (ary.reverse()); //输出:c,b,a

Sort方法是对数组元素排序。有3种使用方式,如下。

数组.sort(); //默认排序数组.sort(排序方式);//使用“排序方式”参数来排序,排序方式是一些定义好的整数常量数组.sort(比较函数);//使用自定义的比较函数,注意此处只写函数名即可

当数组原算是复杂数据类型,且需要根据这种对象的某个属性值来排序时,就需要用到sortOn方法了。比如说,某数组元素都有一个属性值name,希望根据name来排序,那么使用方法如下。

数组.sortOn(“name”); //根据指定属性,默认排序数组.sortOn(“name”,排序方式); //根据指定属性,使用制定的“排序方式”来排序数组.sortOn(“name”,比较函数); //根据指定属性,使用自定义函数排序

1.默认排序sort()

当sort不加任何参数时,说明使用默认排序。默认排序是按照字符串字母顺序排序的,返回值是排序后的数组。

var ary:Array = [“US”,”CHN”,”KOR”];trace (ary.sort()); //输出:CHN,KOR,US

注意,如果数组中的元素是数值,默认排序也会把它当成字符串来排序,而不是按照数值大小,切记。见下例。

var numberAry:Array = [2005,12,1000];//默认排序trace(numberAry.sort());//输出:1000,12,2005//想按数值排序,必须要用排序方式。指定使用NUMERIC 方式排序trace(numberAry.sort(Array.NUMERIC));//输出:12,1000,2005

2.排序方式

所谓排序方式,实际上是一些在Array类中定义好的整形常量,代表几种不同的排序方式。一共有5种,如下。

Array.CASEINSESITIVES值为:1,表示大小写不敏感按字母顺序排序,如果是数字则从小到大

Array.DESCENDING值为:2,表示按字母倒序排列

Array.UNIQUESORT值为:4,表示必须是唯一性排序,如果比较值结果相同,则放弃排序

Array.RETURNINDEXEDARRAY值为:8,让sort()和sortOn()函数返回值排序后的索引列表,同时保持原数组不变

Array.NUMERIC or16值为:16,强制sort()和sortOn()中对数值采用从小到大排序,否则,不设方式,sort()和sortOn()将把数字当成字符串来排序sort和sortOn的返回值和排序方式有关。如果指定Array.RETURNIND EXDARRAY排序方式,那么返回值是排序后的索引值列表;如果指定Array.UNIQUESORT排序方式,要么如果发现有两个或以上元素比较时的比较结果相同,那么返回值为0。其余的排序方式情况下都没有返回值,与默认排序情况相同。

排序方式可以同时用多个,使用"|"号隔开。比较常见是Array.RETURN INDEXEDARRAY和Array.UNIQUESORT与其他排序方式并用。并用格式例子如下。

数组.sort(Array.DESCENDING|Array.UNIQUESORT);//唯一性倒序排列数组.sort(Array.DESCENDING|Array.UNIQUESORT|Array.RETURNINDEXEDARRAY);//返回唯一性倒序排列的索引值数组

示例12-11 展示了上述各种排列方式的运用。

示例 12-11 数组排序方式的运用示例

var ary:Array = ["US","CHINA","china","korea","KOTREA"];//字母倒序方式(默认大小写敏感)trace(ary.sort(Array.DESCENDING));//输出:korea,china,US,KOTREA,CHINA//字母倒序,且大小写不敏感trace(ary.sort(Array.DESCENDING|Array.CASEINSENSITIVE));//输出:US,KOTREA,korea,china,CHINA//大小写不敏感,按默认字母顺序排列,且指定唯一性排序trace(ary.sort(Array.CASEINSENSITIVE|Array.UNIQUESORT));//输出:0,这表示排序失败,因为大小写不敏感后,china 和CHINA 结果一样,导致失去唯一性trace (ary);//输出:US,KOTREA,korea,china,CHINA//果然数组没有发生改变,排序确实失败//去掉大小写不敏感,再按唯一性排序trace(ary.sort(Array.UNIQUESORT));//输出:CHINA,KOTREA,US,china,korea//没有输出0,而是输出排序后数组,表示排序成功trace(ary);//输出:CHINA,KOTREA,US,china,korea//数组发生改变,唯一性排序成功var numberAry:Array = [2005,12.1000];//按数值排序,且指定Array.RETURNINDEXEDARRAY,表示只是返回排序后的索引数组,而不改变原数组内容trace(numberAry.sort(Array.NUMERIC|Array.RETURNINDEXEDARRAY));//输出:1,2,0//返回的排序后索引数组trace(numberAry);//输出:2005,12,1000//可以看出,数组内容确实没有改变//去掉Array.RETURNINDEXEDARRAY,再排序trace(numberAry.sort(Array.NUMERIC));//输出:12,1000,2005//数组内容发生变化,而且确实是原有的索引为1的数字排以第一位,以下同

3.比较函数

示例 12-11 数组排序方式的运用示例

比较函数的要求是,有两个参数,返回值只能是1、0、-1中的一个。为了指代方便,放在第一位的参数,我们称为参数A,放在第二位的称为参数B。格式如下。

function函数名(参数A:类型,参数B:类型):int {…}

比较函数的作用是什么?很简单,当我们指定了比较函数后,程序会自动把数组中的元素一对对地取出来,分别作为这个比较函数的两个参数A和B。如果返回值为1,那么作为A参数的数组元素应当排在B后面;如果返回值为-1,那么A参数元素应当排在B前面;如果返回值为0,表示比较结果一样,不分先后。当返回值为0时,且指定排序方式是Array.UNIQUESORT的话,数组排序就会认为失败并返回返回值0。

比较函数的内容是什么?是将参数A和参数B的一些属性通过某种算法算出结果,并进行比较,返回比较结果。

比如,示例12-12中数组有3个书库存数据对象。想把这3个书库存对象按库存金额从小到大排列。那么,算法就是将单价(price)和书数(number)相乘,如果参数A的乘积小于参数B乘积,那么返回1;如果大于参数B乘积,则返回-1;如果相等,则返回0。

示例12-12数组排序中比较函数的运用

var a:Object = {price:20, number:3};var b:Object = {price:10, number:7};var c:Object = {price:50, number:1};var amountAry:Array = [a,b,c];//比较函数:comparefunction compare(paraA:Object, paraB:Object):int {var resultA = paraA.price*paraA.number;var resultB = paraB.price*paraB.number;if (resultA>resultB) {return 1;}if (resultB<resultA) {return -1;}return 0;}//按比较函数排序amoutAry.sort(compare);trace(amoutAry[0].price);//输出:50trace(amoutAry[1].price);//输出:20trace(amoutAry[1].price);//输出:10

12.3 操作数组每个元素:forEach、every、filter、map、some

在ActionScript 3以前,对数组的各个元素进行操作只能通过for循环和for…in循环来解决。但在ActionScript 3中已经加入了数个有用的方法,能够快速方便地帮我们解决问题。比如,当你好不容易满足了客户的一些书单名称排序、填删书目的要求后,又来了一些更麻烦的需求……,软件开发的现实就是这样残酷,你必须得面对层出不穷的新需求。所以,先看看这些麻烦的需求吧。

● 查一下所有书名,如果发现含有Flash5的书,就标明这个书单有过时书。

● 生成一份新的书单。新书单不仅要包含书名,还要把书的价格写进去。

● 查一下所有书,如果发现有作者是Kingda,就标识这套书选了Kingda的书。

● 将作者为Kingda的书挑出来,生成新的书单。

● 给每个书名都加上"RIA系列"几个字,并把作者名是Kingda的,改成"黑羽"。

确实花样百出。在ActionScript 2中,就只能用多个for循环来解决。代码写得好的,那么这些修改逻辑和具体的数组还能分开,有些代码还能重用;写得不好的,就全部写在for循环里了,一次性使用,下次使用时需要重新再编,从而迷失在重复性体力劳动中。在ActionScript 3中,新增加的5个方法让我们免除了不少体力劳动,编程思维更加直接。而且更重要的是,这些方法将针对各个元素的操作逻辑归到了独立的回调函数中,将总体比较的逻辑写在总的方法意图里。这样使代码重用更加便利。

12.3.1 5 个方法的共同点

这些方法的一个共同之处,就是要使用回调函数来操作数组中的每个元素。ActionScript 3提供了一些新的数组方法来解决这些问题。可以看到这些方法有一些共同之处,那就是参数。第一个参数用来传入一个回调函数(callback function),第二个参数传入一个thisObject。第二个参数不太常用,主要用来指定回调函数的this指向,只有回调函数是由函数表达式定义法定义的,才可能使用这个参数,具体见表12-1。

表12-1操作数组每个元素的方法

条件说明遍历操作forEach(callback:Function,thisObject:* = null):void
将符合回调函数条件的元素提出来,构成一个新的数组并返回。
回调函数不需要返回任何值过滤器filter(callback:Function,thisObject:* = null):Array
操作数组中每一项元
回调函数不需要返回任何值映射器map(callback:Function,thisObject:* = null):Array
根据回调函数操作原数组每个元素,并利用回调函数返回的 结果生成新的数组。
回调函数返回单个新数组元素有一个满足则为真some(callback:Function,thisObject:* = null):Boolean
只要有一个元素能让回调函数返回true,则some()返回true,否则为false。
回调函数返回单个元素比较结果,布尔值全员满足才为真every(callback:Function,thisObject:* = null):Boolean
必须所有元素都让回调函数返回true,every()才返回true,否则为false。
回调函数返回单个元素比较结果,布尔值

12.3.2 回调函数

回调函数,就是要对数组中的每一项运行的函数。该函数可以包含简单的比较操作,也可以是更复杂的操作,比如根据数组元素各个属性用特定算法算出比较结果。回调函数有3个参数,即项值、项索引和Array对象。

第一个参数"项值"相当于数组中的单个元素。如果数组中的元素都是同类元素,则可以将数据类型设为同类类型,否则,设为默认"*"。第二个参数"项索引"必须是整型值,代表着单个元素的索引。第三个参数"Array对象"是当前数组引用传给回调函数。

格式:

function回调函数名(元素标识符:类型,索引:int,要操作数组对象:Array):返回值{}

例子:

function callback(item:*, index:int,array:Array):void;

12.3.3 代码示例

以forEach方法为例,forEach可以在数组中每个元素上执行指定的函数。这个指定的函数称为回调函数。forEach的使用格式如下:

数组.forEach(回调函数); //当回调函数是函数语句定义时,使用这种方式。这也是最常用的使用格式数组.forEach(回调函数,thisObject): //当回调函数是函数表达式定义时,可指定回调函数绑定的this关键字所指的对象

使用函数语句定义的回调函数是封闭定义(method closure)。如果采用函数表达式定义,就是封闭的回调函数。

封闭的回调函数,本身已经将this关键字的指向记住了;而非封闭的回调函数,this关键字指向可以通过第二个参数thisObject来指定。一旦使用封闭回调函数,那么不能再指定thisObject,必须省略,或设为null。具体参见示例12-13。

示例12-13数组forEach、every、filter、map、some:对每个元素进行操作

var book1:Object = {name:"ActionScript 3 殿堂之路",author:"Kingda"};var book1:Object = {name:" Flex 3 殿堂之路",author:"Kingda"};var book1:Object = {name:"AIR 殿堂之路",author:"Kingda"};var book1:Object = {name:"Flash 5 讲解",author:"Kingda"};var book1:Object = {name:"ActionScript 3 Pattern",author:"Kingda"};var booklist:Array = [book1,book2,book3,book4,book5];var price:Array = [100,200,300,400,500];//every用法:如果发现有书名含有Flash5,就表示这套书有过时书trace("这套书没有过时书?" + booklist.every(noFlash5));/*输出:这套书没有过时书?false*/function noFlash5(item:*,index:int,arr:Array):Boolean {if (item.name.indexOf("Flash 5") != -1) {trace("第"+ (index+1) + "本过时了");return false;}return false;}//map的用法:新生成一个书单,将price数组中的价格加入var newBookList:Array = booklist.map(mapNewList);trace(newBookList[0].price);//输出:100function mapNewList(item:*,index:int,arr:Array):Object {var newBook:Object = {};newBook.name = item.name;newBook.author = item.author;newBook.price = price[index];return newBook;}//some的用法:如果发现有作者是Kingda,就标识这套书选了Kingda 的书trace("这套书有Kingda写的吗?" + newBookList.some(isKingda));//输出:这套书有Kingda写的吗?truefunction isKingda(item:*,index:int,arr:Array):Boolean {if (item.author == "Kingda") {return true;}return false;}//filter 的用法:将作者为Kingda 的书挑出来,生成新的书单var kingdaBookList:Array = newBookList.filter(isKingda);trace(kingdaBookList[2].author);//输出:Kingdatrace(kingdaBookList.length);//输出:3//forEach 用法:给每个书名都加上"RIA 系列"几个字,并把作者名是Kingda的,改成"黑羽"kingdaBookList.forEach(addBookHead);//回调函数function addBookHead(item:*,index:int,arr:Array):void {item.bookname = "RIA系列:"+ item.bookname;item.author = "黑羽";}for each (var I in kingdaBookList) {trace("* Book:" + i.bookname+"Author"+ i.author);}/*输出:* Book:RIA系列:ActionScript 3 殿堂之路Author:黑羽* Book:RIA系列:Flex 3 殿堂之路Author:黑羽* Book:RIA系列:AIR 殿堂之路Author:黑羽*/

12.4 本章小结

在ActionScript 3中Array的功能大大增强,多增加的API都非常实用。尤其是灵活的forEach等枚举操作给日常编程减少了很多工作量。望读者能够记住这些Array的使用方法,在编程中经常想到如何去使用它们。

原创粉丝点击