引用类型简介
引用类型的值(对象)是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当(虽然不妥当,在别人提起的时候,就是指ECMAScript中的引用类型,在一次面试的时候,有人就和面试官争论说“JS中没有‘类’这个概念,结果可想而知……”)。尽管ECMAScript从技术上讲是一门面向对象的语言,但它不举杯传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。
如前所述,对象是某个特定应用类型的实例。新对象是使用new操作符后跟一个构造函数来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。如:
var person = new Object();
这行代码创建了Object引用类型的一个新实例,然后把该实例保存在了变量person中。使用的构造函数是Object,它只为新对象定义了默认的属性和方法。ECMAScript提供了很多原生引用类型(如Object),以便开发人员用以实现常见的计算任务。
Object类型
到目前为止,我们看到的大多数引用类型值都是Object类型的实例;而且,Object也是ECMAScript中使用最多的一个类型。虽然Object的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们确实是非常理想的选择。
创建Object实例的方式有两种。第一种是使用new操作符后跟Object构造函数,如下所示:
var person = new Object();
person.name = “Nicholas”;
person.age = 29;
另一种方式是使用对象字面量表示法。对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。如:
var person = {
name:”Nicholas”,
age:29
};
在这个例子中,左边的花括号({)表示对象字面量的开始,因为它出现在了表达式上下文中。ECMAScript中的表达式上选文指的是能够返回一个值(表达式)。复制操作符表示后面是一个值,所以左花括号在这里表示一个表达式的开始。同样的花括号,如果出现在一个语句上下文中,例如跟在if语句条件的后面,则表示一个语句块的开始。
然后,定义了name属性,之后是一个冒号,再后面是这个属性的值。在对象字面量中,使用逗号老分隔不同的属性,因此”Nicholas”后面是一个逗号,但是,在age属性的值29后面不能添加逗号,因为age是这个对象的最后一个属性。在最后一个属性后面添加逗号,会在IE7及更早版本和Opera中导致错误。逗号的作用是分隔,最后一个属性的后面已经没有要分隔的必要了,所以最后一个属性值的后面没有逗号。
在使用对象字面量语法时,属性名也可以使用字符串,如:
var person = {
“name”:”Nicholas”,
“age”:29,
5:true
};
这个例子会创建一个对象,包含三个属性:name、age和5。但这里的数值名会自动转换为字符串。
另外,使用对象字面量语法时,如果留空其花括号,则可以定义只包含默认属性和方法的对象,如:
var person = {};
person.name = “Nicholas”;
person.age = 29;
这个例子与第一个person定义是等价的,只不过看起来有点奇怪。在通过对象字面量定义对象时,实际上不会调用Object构造函数。
虽然可以使用前面介绍的任何一种方法来定义对象,但开发人员更青睐字面量语法,因为这种语法要求的代码量更少,而且能给人封装数据的感觉。实际上,对象字面量也是向函数传递大量可选参数的首选方式,例如:
function dispalyInfo(args){ var output = ""; if(typeof args.name == "string"){ output += "Name:" + args.name + "\n"; } if(typeof args.age == "number"){ output +="Age:" + args.age + "\n"; } alert(output); } dispalyInfo({ name:"Nicholas", age:29 }); dispalyInfo({ name:"Greg" });
在这个例子中,函数displayInfo()接收一个名为args的参数。这个参数可能带有一个名为name或age的水泥工,也可能两个属性都有或都没有。在这个函数内部,通过typeof操作符来检测每个属性是否存在,然后再基于相应的属性来构建一条要显示的消息。然后,调用了两次这个函数,每次都使用一个对象字面量来指定不同的数据。这两次调用传递的参数虽然不同,但函数都能正常执行。
一般来说,访问对象属性时使用的都是点表示法,这也是很多面向对象语言中通用的语法。不过,在JavaScript也可以使用方括号表示法来访问对象的属性。在使用方括号语法时,应该将要访问的属性以字符串的形式放在方括号中,如:
alert(person[“name”]);
从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要有点是可以通过变量来访问属性,如:
var propertyName = “name”;
alert(person[propertyName]);
如果属性名包含会导致语法错误的字符,或者属性名使用的关键字或保留字,也可以使用方括号表示法。如:
person[“first name”];
由于first name中包含一个空格,所以不能使用点表示法来访问它。然而,属性名中是可以包含非字母非数字的,这种时候就可以使用方括号表示法来访问它们。
通常,除非必须使用变量来访问属性,否则建议使用点表示法。
Array类型
除了Object之外,Array类型恐怕是ECMAScript中最常用的类型了。而且,ECMAScript中的数组与其他多数语言中的数组有着很大的区别。虽然ECMAScript数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript数组的每一项可以保存任何类型的数据。也就是说,可以用数组的第一个位置来保存字符串,用第二个位置来保存数值,用第三个位置来保存对象,依次类推。而且,ECMAScript数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。
创建数组的基本方式有两种。第一种是使用Array构造函数,如:
var colors = newArray();
如果预先知道数组包保存的项目数量,也可以给构造函数传递该数量,而该数量会自动变成length属性的值。如:
var colors = newArray(20);
也可以向Array构造函数传递数组中更应该包含的项:
var colors = newArray(“red”,”blue”,”green”);
当然,给构造函数传递一个值也可以创建数组。但这时候问题就复杂了,因为如果传递的数值,则会按照该数值创建包含给定项数的数组;而如果传递的是其他类型的参数,则会创建包含那个值的只有一项的数组。
另外,在使用Array构造函数时也可以省略new操作符。如:
var colors =Array(20);
创建数组的第二种基本方式是使用数组字面量表示法。数组字面量由一堆包含数组项的方括号表示,多个数组项之间以逗号隔开,如:
var colors = [“red”,”blue”,”green”];
var names = [];
var values =[1,2,];
var options =[,,,,,];
以上代码的第一行创建了一个包含3个字符串的数组。第二行使用一堆空方括号创建了一个空数组。第三行展示了在数组字面量的最后一项添加逗号的结果:在IE中,values会成为一个包含3个项且每项的值分别为1,2和undefined的数组;在其他浏览器中,values会成为一个包含2项且值分别为1和2的数组。原因是IE8即之前版本中的ECMAScript实现在数组字面量方面存在bug。由于这个bug导致的另一种情况如最后一行代码所示,该行代码可能会创建爱你包含5项的数组(在IE9+、Firefox、Opera、Safari和Chorme中),也可能会创建包含6项的数组(在IE8及更早版本中)。在像这种省略值的情况下,每一项都将获得undefined值,这个结果与调用Array构造函数时传递项数在逻辑上是相同的。但是由于IE的实现与其他浏览器不一致,因此建议不要使用这种语法。
在读取和设置数组的值时,要使用方括号并提供相应值的基于0的数字索引。如:
var colors = ["red","blue","green"]; alert(colors[0]);//red colors[2] = "black"; colors[5] = "white"; alert(colors[4]);//undefined
方括号中的索引表示要访问的值。如果索引小于数组中的项数,则返回对应项的值,就像这个例子中的colors[0]会显示”red”一样。设置数组的值也使用相同的语法,但会替换指定位置的值。如果设置某个值的索引超过了数组现有项数,如这个例子中的colors[5]所示,数组就会自动增加到该索引值加1的长度。没有值的索引,访问到的值为undefined。
数组的索引不一定要使用正数,比如:
alert(colors[1.2]);//undefined
alert(colors[1.0]);//blue
不过上面的第一个会显示undefined,而第二个会显示colors[1]中对应的值。这也就说明了JavaScript中对索引不进行检查,且会进行类型转换,如:
alert(colors[“1”]);//blue
但是,转换过程只有一次,如:
alert(colors[“1.0”]);//undefined
上面的语句会得到undefined,原因是将”1.0”转换成数值1.0后,得到的结果仍然不是符合条件的数组索引——整数,且只转换成1.0,不再进行将1.0转换成整数1。索引值可以是任何类型,但是除了整数值、可以转换成整数值的浮点数值以及不带浮点的数值的字符串形式,其余的返回值都为undefined。
数组的项数保存在其length属性中,这个属性始终会返回0或更大的值。数组的length属性很有特点——它不是只读的。因此,通过设置这个属性,可以从数组的末尾移除项或项数组中添加新项。如:
var colors = ["red","blue","green"]; var names = []; colors.length = 2; alert(colors[2]);//undefined
这个例子中的数组colors一开始有3个值。将其length属性设置为2会移除最后一项(位置为2的那一项),结果再访问colors[2]就会显示undefined了。如果将其length属性设置为大于数组项数的值,则新增的每一项都会取得undefined值。
利用length属性也可以方便地在数组末尾添加新项,如:
var colors = ["red","blue","green"];//创建一个包含3个字符串的数组 colors[colors.length] = "black";//(在位置3)添加一种颜色 colors[colors.length] = "brown";//(在位置4)再添加一种颜色
由于数组最后一项的索引始终是length-1,因此下一个新项的位置就是length。没当在数组末尾添加一项后,其length属性购回自动更新以反应这以变化。换句话说,上面例子第二行中的colors[colors.length]为位置3添加了一个新值,最后一行的colors[colors.length]为位置4添加了一个新值。当把一个值放在超出当前数组大小的位置上时,数组就会重新计算其长度,即长度值等于最后一项的索引加1。如:
var colors = ["red","blue","green"];//创建一个包含3个字符串的数组 colors[99] = "black";//(在位置99)添加一种颜色 alert(colors.length);//100
检测数组
自从ECMAScript3做出规定以后,就出现了确定某个对象是不是数组的经典问题。对于一个网页,或者一个全局作用域而言,使用instanceof操作符就能得到满意的结果:
if(value instanceof Array){
//对数组执行某些操作
}
instanceof操作符的问题在于,它假定单一的全局执行环境。如果在网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从存在两个以上不同版本的Array构造函数。如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。
为了解决这个问题,ECMAScript5新增了Array.isArray()方法。这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。
转换方法
所有对象都具有toLocalString()、toString()和valueOf()方法。其中,调用数组的toString()方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。而调用valueOf()返回的还是数组。实际上,为了创建这个字符串会调用数组每一项的toString()方法。如:
var colors = ["red","blue","green"];//创建一个包含3个字符串的数组 alert(colors.toString());//red,blue,green alert(colors.valueOf());//red,blue,green alert(colors);//red,blue,green
在这里,首先显式地调用了toString()和valueOf()方法,以便返回数组的字符串表示,每个值的字符串表示拼接了一个字符串,中间以逗号分隔。对吼一行代码直接将数组传递给了alert()。由于alert()要接收字符串参数,所以它在后台会在后台调用toString()方法,由此会得到与直接调用toString()方法相同的结果。
另外toLocaleString()方法经常也会返回与toString()和valueOf()方法相同的值,但也不总是如此。当调用数组的toLocaleString()方法时,它也会创建一个数组值的以逗号分隔的字符串。而与前两个方法唯一的不同之处在于,这一次为了取得每一项的值,调用的是每一项的toLocaleString()方法,而不是toString()方法。如:
var person1 = { toLocaleString:function(){ return "Nikolaos"; }, toString:function(){ return "Nicholas"; } }; var person2 = { toLocaleString:function(){ return "Grigorios"; }, toString:function(){ return "Greg"; } }; var people = [person1,person2]; alert(people);//Nicholas,Greg alert(people.toString());//Nicholas,Greg alert(people.toLocaleString());//Nikolaos,Grigorios
在这里定义了两个对象:person1和person2。而且还分别为每一个对象定义了一个toString()方法和一个toLocaleString()方法,这两个方法返回不同的值。然后,创建一个包含前面定义的两个对象的数组。在数组传递给alert()时,输出结果是Nicholas,Greg,因为调用了数组每一项的toString()方法。而当调用数组的toLocaleString()方法是,输出的结果是Nikolaos,Grigorios,原因是调用了数组每一项的toLocaleString()方法。
数组继承的toLocaleString()、toString()和valueOf()方法,在默认情况下都会以逗号分隔的字符串的形式返回数组项。而如果使用join()方法,则可以使用不同的分隔符来构建这个字符串。join()方法只接收一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。如:
var colors = ["red","blue","green"]; alert(colors.join("-"));//red-blue-green alert(colors.join("**"));//red**blue**green
如果不给join()方法传入任何值,或者给它传入undefined,则使用逗号作为分隔符。IE7及更早版本会错误的使用字符串undefined作为分隔符。如果给join()方法传入null,则会使用字符串null作为分隔符(不知是不是一个bug,其行为应该同undefined及不传递任何参数时的情形——使用默认的逗号进行分隔,可能会在新版本中更新)。
如果数组中的某一项的值是null或undefined,那么该值在join()、toLocaleString()、toString()和valueOf()方法返回的结果中以空字符串表示。
栈方法
ECMAScript数组也提供了一种让数组的行为类似于其他数据结构的方法。具体来说,数组可以表现的就像栈一样,后者是一种可以限制插入和删除项的数据结构。栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入(叫做推入)和移除(叫做弹出),值发生在一个位置——栈的顶部。ECMAScript为数组专门提供了push()和pop()方法,以便实现类似栈的行为。
push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop()方法则从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
队列方法
栈数据结构的访问规则是LIFO(后进先出),而队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出)。队列在列表的末端添加项,从列表的前端移除项。由于push()是向数组末端添加项的方法,因此要模拟队列只需一个从数组前端取得项的方法,实现这一操作的数组方法就是shift(),它能够移除数组中的第一个项并返回该项,同时将数组长度减1。结合使用shift()和push()方法,可以像使用队列一样使用数组。
ECMAScript还为数组提供了一个unshift()方法。顾名思义,unshift()与shift()的用途相反:它能在数组前端添加任意个项并返回新数组的长度。因此,同时使用unshift()和pop()方法,可以从相反的方向来模拟队列,即在数组的前端添加项,从数组末端移除项。
重排序方法
数组中已经存在两个可以直接用来充排序的方法:reverse()和sort()。reverse()方法会反转数组。sort()方法默认情况下按升序排列——即最小的值位于最前面,最大的值排在最后面。为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。即使数组中的每一项都是数值,sort()方法比较的也是字符串,如:
var numbers = [0,1,5,10,15]; numbers.sort(); alert(numbers);//0,1,10,15,5
可见,即使例子中值的顺序没有问题,但sort()方法也会根据测试字符串的结果改变原来的顺序。因为数值5虽然小于10,但在进行字符串比较时,”10”则位于”5”的前面,于是数组的顺序就被修改了。这种排序方式在很多情况下都不是最佳方案。因此sort()方法可以接收一个比较函数作为参数,以便指定哪个值位于哪个值的前面。
比较函数接收两个参数,如果第一个参数应该位于第二个参数之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回一个正数。如:
function compare(value1 , value2){ if(value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } } var numbers = [0,1,5,10,15]; var characters = ["A" , "a" , "e" , "b"]; characters.sort(compare); numbers.sort(compare); alert(numbers);//0,1,5,10,15 alert(characters);//A,a,b,e
这个函数可以适用于大多数数据类型,只要将其作为参数传递给sort()方法即可,在将比较函数传递到sort()方法之后,数值仍然保持了正确的升序。当然,也可以通过比较函数产生降序的结果,只要交换比较函数返回的值即可。
function compare(value1 , value2){ if(value1 < value2){ return 1; }else if(value1 > value2){ return -1; }else{ return 0; } }
对于数值类型或者其valueOf()方法会返回数值类型的对象类型,可以使用一个更简单的比较函数。这个函数只要用第二个值减第一个值即可。如:
function compare(value1 , value2){ return value2-value1; } var person1 = { name:"Nicholas", age:29, valueOf:function(){ return this.age; }, toString:function(){ return "[" + this.name + "; " + this.age + "]"; } }; var person2 = { name:"Greg", age:21, valueOf:function(){ return this.age; }, toString:function(){ return "[" + this.name + "; " + this.age + "]"; } }; var person3 = { name:"Gos", age:25, valueOf:function(){ return this.age; }, toString:function(){ return "[" + this.name + "; " + this.age + "]"; } }; var people = [person1 , person2 , person3]; people.sort(compare); alert(people);//[Nicholas; 29],[Gos; 25],[Greg; 21] var numbers = [0,1,5,10,15]; numbers.sort(compare); alert(numbers);//15,10,5,1,0
操作方法
ECMAScript为操作已经包含在数组中的项提供了很多方法。其中,concat()方法可以基于当前数组中的所有项创建一个新数组。具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给concat()方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给concat()方法的是一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的值不是数组,这些值就会被简单地添加到结果数组的末尾。
var arr0 = [1,2]; var arr1 = [4,5,[6,[7,8,9,"10"]]]; var arr2 = arr0.concat(3 , arr1 , "11"); alert(arr0);//1,2 alert(arr2);//1,2,3,4,5,6,7,8,9,10,11
以上代码开始定义了一个包含两个值的数组arr0,然后,定义了一个比较复杂的数组arr1——数组的项中包含数组(该数组的项中仍然包含数组),接下来基于arr0调用concat()方法,并传入数值3和arr1,以及一个字符串”11”。最终结果数组arr2包含了1,2,3,4,5,6,7,8,9,10,11。也就是concat()方法会将传入的所有数组一层一层的解开直至每一项,然后将这些项添加到数组的末尾。至于原来的数组arr0
,其值保持不变。
下一个方法是slice(),它能够基于当前数组中的一或多个项创建一个新数组。slice()方法可以接受一或两个参数,即要返回项的其实和结束位置。在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回其实和结束位置之间的项——但不包含结束位置的项。slice()方法仍然不会影响原始数组。如果传入的参数大于原始数组length属性值,则会将大于原始数组length属性值的参数替换为原始数组的length属性值,如:
var arr0 = [1,2,3]; var arr1 = arr0.slice(5); var arr2 = arr0.slice(1,5); alert(arr1.length);//0 alert(arr2);//2,3
在上面的代码中,先定义了一个长度为3的数组arr0,接下来基于arr0调用slice()方法。第一次调用时传入了一个参数5,此时5>3,则创建新数组arr1时,等同于语句:
var arr1 = arr0.slice(3);
第二次调用slice()方法时,传入两个参数1和5,5>3,同上,arr2的创建语句等同于:
var arr2 = arr0.slice(1,3);
如果不传入参数,或是传入调用valueOf()方法后返回的不是整数值的参数,则等同于:
arr0.slice(0);
即所有的不合规范的参数都被默认值0替换。
而如果传入的参数是一个负数,则返回从数组的末尾开始,向前参数绝对值个项,如:
var arr0 = [1,2,3];
var arr1 = arr0.slice(-2);
alert(arr1);//2,3
接下来是splice()方法,这个方法恐怕要算是最强大的数组方法了,它有多种用法。splice()的主要用途是向数组的中部插入项,但使用这种方法的方式有如下3种。
(1) 删除:可以删除任意数量的项,只需指定两个参数:要删除的第一项的位置和要删除的项数。例如:splice(0,2)会删除数组中的前量项。
(2) 插入:可以向指定位置插入任意数量的项,只需提供3个参数:其实位置、0(要删除的项数)和要插入的项。如果插入多个项,可以再传入第四、第五,以至任意多个项。例如:splice(2,0,”red” , “green”),会从当前数组的位置2开始插入字符串”red”和”green”。
(3) 替换:可以项指定位置插入任意数量的项,同时删除任意数量的项,只需指定3个参数:其实位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice(2,1,”red”,”green”)会删除当前数组位置2的项,然后再从位置2开始插入字符串”red”和”green”。
splice()方法始终会返回一个数组,该书组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。如:
var numbers = [0,1,2,3]; var removed = numbers.splice(0,1);//删除第一项 alert(numbers);//1,2,3 alert(removed);//0 removed = numbers.splice(1,0,8,9); alert(removed);//返回的是一个空数组 removed = numbers.splice(1,1,5,6); alert(removed);//8
位置方法
ECMAScript5为数组实例添加了两个位置方法:indexOf()和lastIndexOf()。这两个函数都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中,indexOf()方法从数组的开头(位置0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。
这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回-1。在比较第一个参数与数组中的每一项时,会使用全等操作符;也就是说,要求查找的项必须严格相等。
迭代方法
ECMAScript5为数组定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响访问的返回值。以下是这5个迭代方法的作用。
(1) every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
(2) filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。
(3) forEach():对数组中的每一项运行给定函数,这个方法没有返回值。
(4) map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
(5) some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
以上方法都不会修改数组中的包含的值。
在这些方法中,最相似的是every()和some(),它们都用于查询数组中的项是否满足某个条件。对于every()来说,传入的函数必须对每一项都返回true,这个方法菜返回true;否则,它就返回false。而some()方法则只要传入的函数对数组中某一项返回true,就会返回true。如:
var numbers = [0,1,2,3]; var everyResult = numbers.every(function(item , index , array){ return (item > 2); }); alert(everyResult);//false var someResult = numbers.some(function(item , index , array){ return (item > 2); }); alert(someResult);//true
以上代码调用了every()和some(),传入的函数只要给定项大于2就会返回true。对于every(),它返回的是false,因为只有部分数组项符合条件。对于some(),结果就是true,因为至少有一项是大于2的。
filter()方法的作用是检索出数组中满足给定条件的项,如下面的代码中,查询出年龄大于24周岁的人员信息:
function compare(value1 , value2){ return value2-value1; } var person1 = { name:"Nicholas", age:29, valueOf:function(){ return this.age; }, toString:function(){ return "[" + this.name + "; " + this.age + "]"; } }; var person2 = { name:"Greg", age:21, valueOf:function(){ return this.age; }, toString:function(){ return "[" + this.name + "; " + this.age + "]"; } }; var person3 = { name:"Gos", age:25, valueOf:function(){ return this.age; }, toString:function(){ return "[" + this.name + "; " + this.age + "]"; } }; var people = [person1 , person2 , person3]; var oldPeople = people.filter(function(item , index , array){ return item>24; }); alert(oldPeople);//[Nicholas; 29],[Gos; 25]
在上面的例子中一个person对象能够与数值24进行比较是因为,在对象的定义中定义了valueOf()方法,而在比较时会调用对象的valueOf()方法,返回的是person的age属性值,进而过滤出符合条件(>24)的person。
map()方法也返回一个数组,则这个数组的每一项都是在原始数组中的对应项上运行传入函数的结果。例如,给数组中的每一项乘以2(所有人的工资翻倍,嗯),然后返回这些乘积组成的数组,如:
var salary = [7800,6000,2300]; var doubledSalary = salary.map(function(item , index , array){ return item*2; }); alert(doubledSalary);//15600,12000,4600
最后一个方法是forEach(),它只是对数组中的每一项运行传入的函数,这个方法没有返回值,本质上与使用for循环迭代数组一样。如:
var nums = [1,2,3,4,5,6]; nums.forEach(function(item,index,array){ nums[index] = item*2; //或是进行其他操作 }); alert(nums);//2,4,6,8,10,12
得到这样的结果与前面说的这5个方法不会改变数组的值是否矛盾,不矛盾,因为上面的例子改变数组值的不是forEach()方法,而是代码nums[index] = item*2。
这些方法通过执行不同的操作,可以大大方便处理数组的任务。支持这些迭代方法的浏览器有IE9+、Firefox2+、Safari3+、Opera9.5和Chrome。
缩小方法
ECMAScript5还增加了两个缩小数组的方法:reduce()和reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()方法从数组的第一项开始,逐个遍历到最后。而reduceRight()则从数组的最后一项开始,项前遍历到第一项。
这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为缩小基础的初始值。传给reduce()和reduceRight()的函数接收4个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。
使用reduce()方法可以执行求数组中所有值之和的操作,如:
var nums = [1,2,3,4,5,6]; var sum = nums.reduce(function(prev, cur , index , array){ return prev + cur; }); alert(sum);//21
reduceRight()的作用类似,只不过方向相反而已。
支持这两个缩小方法的浏览器有IE9+、Firefox3+、Safari4+、Opera10.5和Chrome。