首页 > 网站 > WEB开发 > 正文

第七章:Javascript数组

2024-04-27 14:13:01
字体:
来源:转载
供稿:网友

第七章:javascript数组

数组是值的有序结合。每个值叫做一个元素,而每个元素在数组中都有一个位置,用数字表示,称为索引。

Javascript数组是无类型的:数组的元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类型。数组的元素可能是对象或其他数组,这允许创建更加复杂的数据结构,如对象的数组和数组的数组。

javascript数组的索引是基本零的32位数值:第一个元素的索引为0,最大的索引为4 294 967 294(2的32次方-2),数组最大能容纳4 294 967 295个元素。

javascript数组是动态的:根据需要他们会增长或缩减,并且在创建数组时无需声明一个固定大小或者在数组大小变化时无需从新分配空间,javascript数组可能是稀疏的:数组元素的索引不一定要连续的,该属性就是数组元素的个数。征对稀疏数组,length比所有元素的索引要大。

javascript数组是javascript对象的特殊形式,数组索引实际上和碰巧是整数的属性名差不多。我们将在本文(章)更多讨论特殊化的数组。通常数组的实现是经过优化的,用数组索引来访问数组元素一般来说比访问常规的对象属性要快很多。

数组继承自Array.PRototype的属性,它定义了一套丰富的数组操作方法,8和9节涵盖这方面的内容。大多这些方法是通用的,这意味着他们不仅对正真的数组有效,而且对“类数组对象”同样有效。11节讨论类数组对象。在ECMAScript5中,字符串的行为与字符数组类似,12节将讨论。

1.创建数组

使用直接量创建数组是最简单方法,在方括号中将组组元素用逗号隔开即可,如:

            var empty = [];            var primes = [2, 3, 5, 7, 11];            var misc = [1.1, true, "a", ]; //三个不同类型的元素和结尾的逗号

数组直接量中的值不一定是要常量,它们可以是任意的表达式

            var base = 1024;            var table = [base, base + 1, base + 2, base + 3]

它可以包含对象直接量或其他数组直接量

        var b = [[1,{x:1,y:2}],[2,{x:3,y:4}]];

如果省略数组直接量中的某个值,省略的值的元素将被赋予undefined

            var count = [1, , 3] //数组中有三个元素,第二个值为undefined            var undefs = [, , ] //两个元素,都是undefined

调用构造函数Array()是创建数组的令一种方法。用三种方法调用构造函数

  • 调用时没有参数
  • 调用时有一个值是参数,它指定长度
  • 显式指定两个或多个数组元素或者数组的一个非数组元素
        //调用时没有参数        var a =new Array();

该方法创建一个没有任何元素的空数组,等于数组直接量[].

            var a = new Array(10);

该方法创建指定长度的数组,当预先知道所需元素的个数时,这种形式的Array()构造函数可以用来分配一个数组空间。注意,数组中没有存储值。甚至数组的索引,“0”,“1”等还未定义。

            var a = new Array(5, 4, 3, 2, 1, "testing,testing")

用这种形式,构造的函数参数将会成为新的数组元素,使用数组字面量比这样使用Array()构造函数要简单多了。

2.数组元素的读和写

使用[]操作符来访问数组中的一个元素。数组的引用位于方括号的左边。方括号中是一个返回非负整数值的任意表达式。使用该表达式可以读也可以写数组中的一个元素。因此,如下的代码都是合法的javascript语句

            var a = ["world"]; //从一个元素的数组开始            var value = a[0]; //读取第0个元素            a[1] = 3.14; //写第1个元素            i = 2;            a[i] = 3; //写第2个元素            a[i + 1] = "hello"; //写第3个元素            a[a[i]] = a[0] //读取第0和第2个元素,写第3个元素

请记住:数组是对象的特殊形式。使用方括号访问数组元素就像使用方括号访问对象的属性一样。javascript将制定的数组索引替换成字符串——索引1变成"1",然后将其作为属性名来使用。关于索引值从数字转换为字符串没有什么特别之处:对常规对象也可以这么做:

        o = {}; //创建一个普通的对象        o[1] = "none" ;//用一个整数来索引它

数组的特别之处在于,当使用小于2的32次方的非负数整数作为属性名时,数组会自动维护其length属性值。如上,创建仅有一个元素的数组,然后在1、2、3、处进行赋值,当我们这么做时,数组的length变为4

清晰地区分数组的索引和数组的属性名是非常有用的。所有的索引都是其属性名,但只有0到(2的32次方-2)的整数属性名才是索引。所有的数组都是对象,可以为其创建任意名字的属性。但如果使用的属性是数组的索引,数组的特殊行为就是将根据需要更新它们的length属性值。

注意,可以使用负数或非整数来索引数组。这种情况下,数值转换为字符串。字符串作为属性名来用。既然名字不是非负整数,就只能当做常规的对象属性,而非数组的所有。同样,如果凑巧使用了是非负整数的字符串,它就当做数组索引,而非对象的属性。当使用一个浮点数和一个整数相等时情况也是一样的:

            a[-1.23] = true; //这将创建一个名为"-1.23"的属性            a["1000"] = 0; //这是数组的第1001个元素            a[1.000] //和a[1]相等

事实上数组索引仅仅是对象属性名的一种特殊类型,这意味着javascript数组没有“越界”错误的概念。当试图查询对象中不存在的属性时,不会报错。只会得到undefined值。类似于对象,对于对象同样存在这种情况。

既然数组是对象 ,那么它们可以从原型中继承元素。在ECMAScript5中,数组可以定义元素的getter和setter方法(6.6)。如果一个数组确实继承了元素或使用了元素的getter和setter方法,你应该期望它使用非优化的代码路径:访问这种数组的元素的时间会与常规对象属性的查找时间相近。

3.稀疏数组

稀疏数组就是包含从0开始的不连续索引的数组。通常,数组的length属性值代表了数组中元素的个数。如果数组是稀疏的,那么length的值远大于元素的个数。可以使用Array构造函数或简单地指定数组的索引值大于当前数组长度来创建稀疏数组。

        a = new Array(5);//数组中没有元素,但a.length值是5        a = []; //创建一个空数组,length的值为0        a[1000] = 0;//赋值添加一个元素,但设置的length值为1001

后面你也可以delete操作符产生稀疏数组。

足够稀疏的数组通常在实现上比稠密是数组更慢、内存利用率更高,在这样的数组中查找元素的时间与常规对象属性的查找时间一样长。

注意,当数组直接量中省略值时不会创建稀疏数组。省略的元素在数组中是存在的,其值为undefined。这和数组元素根本不在是有一些微妙的区别的。可以用in操作符检测两者的区别。

            var a1 = [, , , ]; //数组是[undefined,undefined,undefined]            var a2 = new Array(3); //该数组根本没有元素            0 in a1; //=>true a1在索引0处有一个元素;            0 in a2; //=>false a2在索引0处没有元素

当使用for/in循环时,a1和a2的区别也很明显(第6节)

需要注意的是,当省略数组直接量中的值时(使用连续的逗号,比如[1,,,,3]),这时得到的也是稀疏数组,省略掉的芝是不存在的:

            var a1 = [, ]; //此时数组没有元素,长度为1            var a2 = [undefined]; //此时数组包含一个值为undefined的元素            0 in a1; //=>false: a1在索引0处没有元素            0 in a2; //=>true: a2在0处有一个值为undefined的元素

在一些旧版的实现中,如(Firefox3),在存在连续逗号的情况下,插入undefined值的操作与此不同,在这些实现中,[1,,3]和[1,undefined,3]是一模一样的。

了解稀疏数组是了解javascript数组真实本质的一部分。尽管如此,实际碰到的绝大多数数组并不是稀疏数组。并且,如果碰到稀疏数组,你的代码很可能像对待非稀疏数组一样去对待他们,只不过他们包含一些undefined值。

4.数组的长度

每个数组都有一个length属性。就是这个属性使其区别于常规的javascript。征对稠密数组,length属性值代表了元素中的个数,其值比数组中的最大索引数大1:

            [].length //=>0:数组没有元素            ['a', 'b', 'c'].length //=>3 最大是索引为2,length为3

当数组是稀疏的时,length的值远远大于元素的个数。而且关于此我们就可以说的一切也就是数组长度保证大于它的每个元素的索引值。换一种话说,在数组中,肯定找不到一个元素的索引值大于它的长度。为了维持此规则不变,数组有两个特殊行为。第一个如同上面的描述:如果为一个数组元素赋值,它的索引i大于或等于现有的数组的长度时,length的值将设置为i+1.

第二个特殊行为就是设置length属性为一个小于当前长度的非负整数n时,当前数组中的那些索引值大于或等于n的元素将从中删除,例:

            var a = [1, 2, 3, 4, 5]; //从5个元素的数组开始            a.length = 3; //现在a为 [1, 2, 3]            a.length = 0; //删除所有的元素a为[]            a.length = 5; //长度为5,但是没有元素,就行new Array(5)

还可以将数组的length属性值设置为大于当前的长度。实际上不会向数组中添加新的元素,它只是在的尾部创建一个空的区域。

在ECMAScript5中,可以使用Object.defineProperty()将数组的属性变成只读的:

            var a = [1, 2, 3];            Object.defineProperty(a, "length", {writable: false});            a.length = 0;            console.log(a);//=> [1, 2, 3]

类似的,如果让一个数组元素不能配置,就不能删除它。如果不能删除它,length的属性不能设置小于不可配置元素的索引值(见6.7节的Object.seal()和Object.freeze()方法)。

5.数组元素的添加和删除

我们已经见过添加数组元素最简单的方法:为新索引赋值

            a = []; //开始是一个空数组            a[0] = "zero"; //想其中添加元素            a[1] = "one";            a;//=> ["zero", "one"]

可以使用push()方法在数组的末尾增加一个或多个元素;

在数组的末尾压入一个元素与给数组a[a.length]赋值是一样的,可以使用unshift()方法给首部插入一个元素,并且将其他元素移动到更高的索引处;

        a = []; //开始是空数组        a.push("zero"); //在末尾添加一个元素。 a = ["zero"]        a.push("one","two");//再添加两个元素        a;//=>["zero", "one", "two"]        a.unshift("start");        a;//=> ["start", "zero", "one", "two"]

可以像删除对象属性一样使用delete运算符来删除数组元素:

            a = [1, 2, 3];            delete a[1];            1 in a; //=>false:数组索引1并未在数组中定义            a.length; //=>3: delete操作并不影响数组的长度

删除数组元素与为其赋值undefined值是类似的(但有一些微妙的区别)。对一个数组元素使用delete不会修改数组的length属性;也不会将元素从高索引处移下来填充已经删除的元素空白。如果从一个数组中删除一个元素,它就变成稀疏数组。

从上文看出,简单设置length属性也可以删除数组尾部的元素。数组由pop()方法(它和push一起使用),后者使减少长度1并返回删除元素的值。还有一个shift()方法(它和unshift()一起使用),从数组头部删除一个元素。和delete不同的是shift()方法将所有的元素下移到比当前元素索引低1的地方(本文8小节涵盖pop()和shift()的内容)。

最后,splice()是一个通用的方法来插入,删除或替换的数组元素方法。它会根据需要修改length属性并移动圆度到更高或较低的索引处(本文8小节详述)。

6.数组遍历

使用for循环(5.5.iii)是遍历数组的常用方法:

var keys = Object.keys(o); //获得o对象属性名组成的数组            var values = []; //在树组中存储匹配属性的值            for (var i = 0; i < keys.length; i++) { //对于数组中的每个索引                var key = keys[i]; //获得索引处的键值                values[i] = o[key]; //在values数组中保存属性值            }

在嵌套循环或其他它性能非常重要的上下文中,可以看到这种基本的数组遍历需要优化,数组的长度应该只是查询一次而非每次循环要查询:

            for (var i = 0, len = keys.length; i < len; i++) {                //循环体仍然不变            }             //从写            var keys = Object.keys(o); //获得o对象属性名组成的数组            var values = []; //在树组中存储匹配属性的值            for (var i = 0, len = keys.length; i < len; i++) { //对于数组中的每个索引                var key = keys[i]; //获得索引处的键值                values[i] = o[key]; //在values数组中保存属性值            }

加速这些数组是稠密的,并且所有的元素都是合法数据。否则,使用数组元素之间都应该检测他们,如果想要排除null、undefined和不存在的元素,代码如下:

                for (var i = 0; i < keys.length; i++) {                    if (!keys[i]) continue;//循环体                }

如果只想跳过undefined和不存在的元素

        for (var i = 0; i < keys.length; i++) {             if (!keys[i] === undefined) continue; //跳过undefined和不存在的元素            //循环体            }

最后,如果只想跳过不存在的元素而仍然要处理存在的undefined的元素,代码如下

            for (var i = 0; i < keys.length; i++) {                if (!(i in keys)) continue; //跳过不存在的元素            }

还可以使用for/in循环(5.4.iiii),处理稀疏数组。循环每次将一个可枚举的属性名(包括数组索引)赋值给循环变量,不存在的索引将不会遍历到:

            for (var index in sparseArray) {                var value = sparseArray[ind
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表