9.2 使用数组
操作数组的方法很简单,但是比较灵活。其灵活性表现在:数组的名称是一个指向数组的引用变量,数组的长度可以随意伸缩;数组的下标可以是表达式;数组的元素可以自由操作。
9.2.1 存取数组元素
存取数组元素值主要通过中括号运算符([])来实现。中括号运算符左侧是对数组的引用,即数组标识符,中括号内指定数组元素的下标。
【示例1】下面的代码使用了中括号运算符存取数组元素的值。
var a=[]; //声明一个空数组直接量,并把它引用给变量a a[0]=1; //为数组第一个元素赋值为1 a[2]=2; //为数组第三个元素赋值为2 alert(a[0]); //读取数组第一个元素的值,返回值为1 alert(a[1]); //读取数组第二个元素的值,返回值为undefined alert(a[2]); //读取数组第三个元素的值,返回值为2
在存取数组元素操作中,应注意以下几个问题。
(1)在JavaScript中,数组长度是弹性的,可以在操作中随时扩展数组的长度。例如,在上面示例中虽然仅给下标为0和2位置的元素赋值,但是JavaScript会自动为下标为1的元素预定义为undefined。
(2)JavaScript数组下标是以0开始的。下标可以是值为非负整数的表达式。
【示例2】下面使用for语句批量为数组元素赋值。
var a=new Array(); //创建一个空数组 for(var i=0; i<10; i++){ //循环为数组赋值 a[i++]=++i; //不按顺序为数组元素赋值 } alert(a); //返回2, , ,5, , ,8, , ,11
在上面示例中,数组下标是一个不断递增的表达式。用户可以使用任意表达式,只要确保表达式的返回值是非负整数即可。
(3)数值下标的值必须是大于等于0,且小于232-1的整数。如果下标值太大,或为负数、浮点数、布尔值、对象及其他值,JavaScript会自动把它转换为一个字符串,从而生成与字符串相关联的关联数组,即作为对象属性的名字,而不再是数组下标。
【示例3】在本示例中,数组下标true将不会被强制转换为数值1, JavaScript会把变量a视为对象,true作为字符串被视为对象的属性名。
var a=[1,2,3]; //声明数组直接量 alert(a[true]); //返回undefined,说明a不是指向数组a,而是指向对象a var a={ //声明对象直接量 "true":1 //属性true的值为1 } alert(a[true]); //返回值为1,说明a确实是指向对象a,并读取属性名为true的值
(4)数组元素可以被添加到对象中。
【示例4】在本示例中,a是一个指向对象的变量,当使用数组下标为其赋值时,JavaScript不再把它看作是数组下标,而是把它看作对象的属性名。
var a={}; //声明对象直接量 a[0]=1; //赋值 alert(a["0"]); //返回1,读取对象a中名称为0的属性值
它相当于一个对象直接量:
var a = { 0 : 1 };
由于0是非法的标识符,所以不能使用如下方式读取属性值。
alert(a.0);
9.2.2 数组长度
在传统语言中,数组一旦被声明,其长度是固定的,但是JavaScript数组就很灵活,数组元素的个数可以进行任意修改。不用考虑所定义的元素是否超出数组的长度,或者下标是否保持连续值。
【示例1】在本示例中,分别为空数组a中下标位置为2和100的元素赋值,此时数组a中仅有两个元素。
var a=[]; //声明空数组 a[2]=1; //为下标为2的元素赋值 a[100]=2; //为下标为100的元素赋值 for(var i in a){ //遍历数组,仅能够读取两个元素,说明其他元素不存在 alert(a[i]); }
JavaScript在初始化数组时,只有那些真正存储在数组中的元素才能够被分配到内存中去。因此,在上面示例中JavaScript解释器只给数组下标为2和100的元素分配内存,而并不给其他下标值的元素分配内存。但是用户可以存取其他下标元素:
alert(a[0]); //返回undefined,说明没有这个元素 alert(a[1]); //返回undefined,说明没有这个元素 a[0]=3; //为数组下标值等于0的元素赋值为3 a[1]=4; //为数组下标值等于1的元素赋值为4 alert(a[0]); //返回3,说明该元素存在 alert(a[1]); //返回4,说明该元素存在
这说明JavaScript数组中的元素值,以及元素个数都是动态的,且元素下标是否为连续值都不重要。
JavaScript为Array对象预定义了一个length属性,该属性将被所有数组对象继承,不管是由Array()构造函数创建的数组,还是由数组直接量定义的数组。length属性存储了数组包含的元素个数。
【示例2】length属性值不是数组元素的实际个数,而是当前数组的最大元素个数。由于数组下标可以是非连续的,因此length属性的值总是等于数组最大下标值加上1。
var a=[]; //声明空数组 a[2] =1; a[100] =2; alert(a.length); //返回101
由于数组下标必须小于232-1,所以length属性值最大等于232-1。
【示例3】数组的length属性是一个动态值,当数组添加了新元素之后,它的值也会自动更新,以便在给数组添加新元时保持不变。
a[200] =200; alert(a.length); //返回201
【示例4】作为动态值,数组的length属性可以被更新。如果length属性被设置了一个比当前length值小的值,则数组将会被截断,新长度之外的元素值都会被丢失;如果length属性被设置了一个比当前length值大的值,那么新的未定义的元素就会被添加到数组末尾,以使得数组增长到新指定的长度,其默认值为undefined。
var a=[1,2,3]; //声明数组直接量 a.length=5; //增长数组长度 alert(a[4]); //返回undefined,说明该元素还没有被赋值 a.length=2; //缩短数组长度 alert(a[2]); //返回undefined,说明该元素的值已经丢失
9.2.3 案例:比较对象与数组
对象(Object)与数组(Array)是两种不同类型的引用型数据。从数据存储的方式上来看,它们非常相似。
【示例1】对象是一种包含已命名的值的集合类型,而数组则是一种包含已编码的值的集合类型。
var o={ //对象 x:1, //该值命名为x y: true //该值命名为y } var a=[ //数组 1, //该值隐含编码为0 true //该值隐含编码为1 ]
【示例2】由于对象的数据存储形式很像数组,因此被称为关联数组,但它不是真正意义上的数组。关联数组就是将值与特定字符串关联在一起。真正的数组与字符串没有联系,但是它将值和数值(非负整数的下标)关联在一起。
alert(o["x"]); //返回1,在对象o中,值1与字符串x关联在一起 alert(a[0]); //返回1,在数组a中,值1与数值0关联在一起
使用点运算符(.)可以存取对象属性,而数组使用中括号([])来存取属性。针对上面对象属性的读取操作,下面两行代码的意思是相同的:
o.x; //返回1,使用点运算符存取属性 o["x"]; //返回1,使用中括号运算符存取属性
提示:使用点运算符存取属性时,属性名是标识符;而使用中括号运算符存取属性时,属性名是字符串。
【示例3】当用点号运算符来存取对象属性时,属性名是用标识符表示的;当用中括号运算符来存取对象属性时,属性名是用字符串表示的,因此可以在运行过程中动态生成字符串。
var o={ //对象直接量 p1 : 1, p2 : true } for(var i=1; i<3; i++){ //循环读取对象的属性值 alert( o["p" + i] ); }
通过采用关联数组法访问带有字符串表达式的对象属性是非常灵活的。当对象属性非常多时,使用点运算符来存取对象属性会比较麻烦。另外,在一些特殊情况下只能使用关联数组形式来存取对象属性。
提示:关联数组是一种数据结构,它允许用户动态地将任意值与任意字符串关联在一起。实际上JavaScript对象就是使用关联数组实现的。这样可以在设计中将对象和数组作为单独的类型来处理。但是要完全掌握对象和数组的行为,用户应该了解数组是一种具有额外功能层的对象。
【示例4】本示例使用typeof运算符提示数组实际上为对象类型,其返回值是字符串"object"。
var a=[1,2,3]; //数组直接量 alert(typeof a); //返回数组类型为Object,而不是Array alert(a.constructor==Array); //返回true,说明数组直接量的构造器是Array
【拓展】在编程语言中,数据集合表示一类数据的总和。集合既是一种数据存储结构,也是一种数据组织结构,更是一种数据处理结构。数据集合有多种类型,说明如下。
数组(Array):固定大小的有序集合。
数组列表(ArrayList):对象的动态数组类型。
列表(List):可通过索引访问的对象的强类型列表。
字典(Dictionary):表示键和值的集合。
有序列表(SortedList):与哈希表类似。
哈希表(Hashtable):名/值对,类似字典,比数组更强大。
栈(Stack):后进先出栈集合。
队列(Queue):先进先出栈集合。
每种类型的集合,其数据存储格式和语法约定都是不同的,这在不同语言中会略有不同。定义多类型的集合,其目的就是为了高效处理数据。
由于JavaScript是一种弱类型语言,它支持数组这种特殊类型的集合形式。不过,用户可以以对象结构支持哈希表结构类型,以及通过数组方式支持栈和队列结构的操作。
哈希表是一种非常重要的数据集合。与数组不同,它是一种无序数据集合,以名/值对的形式存储数据。系统能够根据关键码(即名/值对的名称)映射到表中对应的某个值来访问记录,以加快访问速度。
与数组相比,哈希表查找数据的效率是非常高的。例如,如果查找数组中的一个值为a的元素,数组会从下标为0的起始点开始遍历,最长可能要遍历数组的每一个元素。如果数组的长度很大,那么遍历所花费的时间会非常长。而如果在哈希表中查找一个值为a的元素,可以直接通过码值映射来访问,不需要遍历哈希表中每个元素。
JavaScript没有从语法角度明文定义哈希表数据结构,但是JavaScript对象类型的数据结构与哈希表结构基本相同,因此用户可以使用对象结构来代替哈希表。
【示例5】由于对象的属性可以动态添加和删除,当把对象作为集合来看待时,实际上它就是哈希表。
var h={ //定义对象h,其数据结构就是哈希表结构 x:1, //名/值对1 y:2, //名/值对2 z:3 //名/值对3 }
在上面代码中,就是一个哈希表结构,共包含3个关键码(key):x、y和z。用户可以借助这些关键码以下标形式来访问集合中的数据。
alert(h["x"]); //返回1
关键码以字符串的形式存在,而不是以标识符的形式存在,因此在下标引用时,必须以字符串的语法格式添加引号。
如果借助in运算符还可以检测某个关键码是否存在于哈希表中。
alert("x"in h); //返回true,说明存在
在JavaScript中,可以使用delete运算符删除指定的属性,或者动态增加属性等操作。
【示例6】由于哈希表结构与对象结构完全相同,用户完全可以淡化数据类型的鸿沟,以操作对象的方式操作哈希表。例如,通过构造函数创建哈希表:
var h = new Object();
或者使用对象直接量定义集合结构:
var h = {}; var h ={ x : 1, y : 2 };
另外,还可以使用对象的存取方法操作哈希表。
9.2.4 案例:定义多维数组
JavaScript不支持多维数组,但是多维数组在实际开发中非常实用,用户可以通过数组嵌套的形式定义多维数组。
【示例1】下面代码定义一个二维数组。
var a=[ //定义二维数组 [1,2,3], [4,5,6], [7,8,9] ];
要存取二维数组的元素,只需要使用两次中括号运算符即可。对于多维数组,则方法依此类推即可。
alert(a[0][0]) //返回1,读取第1个元素的值 alert(a[2][2]) //返回9,读取第9个元素的值
在存取多维数组时,左侧中括号内的下标值不能够超出数组实际下标,否则就会提示系统错误:
alert(a[3][2]) //提示编译错误
因为JavaScript解释器是按着从左到右的顺序来存取的,如果第一个下标超出,则元素值为undefined,显然表达式undefined[2]是错误的。
【示例2】本示例演示如何定义一个二维数组来存储1~100的整数值,从而设计一个简单的二维数列。
var a=[] //二维数组 for(var i=0; i<10; i++){ //行循环 var b=[]; //辅助数组 for(var j=0; j<10; j++){ //列循环 b[j]=i*10+j+1; //定义数组b的元素值 } a[i]=b; //把数组b赋值给数组a } alert(a); //返回1~100的二维数列
数列样式如下所示:
a = [ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [31, 32, 33, 34, 35, 36, 37, 38, 39, 40], [41, 42, 43, 44, 45, 46, 47, 48, 49, 50], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60], [61, 62, 63, 64, 65, 66, 67, 68, 69, 70], [71, 72, 73, 74, 75, 76, 77, 78, 79, 80], [81, 82, 83, 84, 85, 86, 87, 88, 89, 90], [91, 92, 93, 94, 95, 96, 97, 98, 99, 100] ];
但是alert(a);提示返回值却是“1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61, 62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96, 97,98,99,100”,这说明不论是一维数组,还是二维数组,对于JavaScript来说,所有数组都是有序排列的,没有为多维数组制订特殊的存储格式。