7.数组
数组是值得有序集合。每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,成为索引。
7.1 数组创建
<pre name="code" class="html">方法一:使用直接量创建数组
var empty = [];//创建一个没有元素的数组var primes = [1,true,{x:"x"},4,5,]//创建一个有5个元素的数组,元素类型不同var count = [1,,3];//数组元素有3三,中间的那个元素值为undefinedvar undefs = [,,]//数组元素有两个,都是undefined //注意:直接量创建数组的语法允许有可选的结尾逗号
方法二:使用构造函Array()创建数组var a = new Array();//等同于var a = [];var a = new Array(10);//创建length等于10的数组。//注意数组中没有存储值,索引属性“0”、“1”等还没有定义var a = new Array(5,4,3,"testing",true);//构造函数的参数将会成为新数组的元素。这里需要注意,如果构造函数只有一个参数,并且是数值类型的,那么这个参数为数组的长度。如果构造函数有两个或多个参数,或只有一个非数值类型的参数是,那么这些参数为数组的元素。
7.2数组元素的读和写
使用[]操作符来访问数组中的一个元素。数组的引用位于操作符的左边。方括号中是一个返回非负整数值得任意表达式。使用该语法既可以读又可以写一个元素。事例代码如下:
var a = ["world"];
var value = a[0];//读第0个元素
a[1] = 3.14;//写第1个元素
请记住,数组是对象的特殊形式。使用方括号访问数组就像使用方括号访问对象属性一样。JavaScript将指定的数字索引转换成字符串----------索引值1变成“1”-------------然后将其作为属性名来使用。
关于索引值从数字转换成字符串没用什么特别之处:常规对象也可以这么做,如下:
var o = {};
o[1] = "one";//相当于给对象o添加了一个“1”属性名
o[1];//获取属性“1“的值
o.1;//但是这种通过点来获取的方式是错误的,不会通过编译
对于通过点(.)运算符来获取对象的属性的值时,运算符左侧是对象的引用,右侧必须是一个以属性名称命名的标示符(标示符必须以字符、下划线(_)、美元符($)号开始)。
对于通过方括号([])运算符来获取对象的属性的值时,运算符左侧是对象的引用,右侧必须是一个计算结果为字符串的表达式。
清晰地区分数组索引和属性名是非常有用的。所有的索引都是属性名,但只有在0~2的32次方-2之间的整数属性名才是索引。
注意:可以使用负数和非整数来索引数组。这种情况下,数值将转换成字符串,字符串作为对象的属性名来使用。数组的length属性不会改变。如下:
var a = [];//别忘了数组也是一个对象,只不过是对象的一种特殊形式而已
a[-10] = "负十";
console.log(a[-10]);//=>负十
console.log(a.length);//=>0
同样,如果使用了非负整数的字符串。这中情况下,非负整数的字符串将会转换成非负整数,非负整数将作为数组的索引,而非对象的属性。如下:
var a = [];
a["0"] = "零";
console.log(a[0]);//=>零
console.log(a.length);//=>1
也就说数组方括号中的值可以隐式的转换成非负整数的,就会被当做数组索引,否则就会被当做数组的属性。在比如:
a[1.000] //1.000可以隐式的转换成非负整数1 //等同于a[1]
7.3稀疏数组
稀疏数组就是包含从0开始的不连续索引的数组。通常,数组的length属性代表数组中元素的个数。如果数组是稀疏的,length属性值大于元素的个数。以下几种方式就可以创建稀疏数组:
方式一:使用Array()构造函数来创建稀疏数组
var a = new Array(5);//数组没有元素,a.length是5
方式二:指定数组的索引值大于当前的数组长度
var a = [];//a.length为0
a[0] = "零";
a[5] = "五";//给数组添加一个元素,a.length为11
console.log(Object.keys(a));//<span style="font-family: Arial, Helvetica, sans-serif;">=></span><span style="font-family: Arial, Helvetica, sans-serif;">["0","5"] //索引不连续,是稀疏数组。这里也可以这么理解-----数组对象a没有属性1、2、3、4、属性名。</span>
方式三:用delete操作符来产生稀疏数组。
var a = [1,2,3];
delete a[1];
console.log(1 in a);//=> false
这一节中有这么一段话和代码,如下:
"注意,当在数组直接量中省略值时不会创建稀疏数组。省略的元素在数组中是存在的,其值为undefined。"
var a1 = [,,,]; //数组是[undefined,undefined,undefined]
var a2 = new Array(3); //该数组根本没有元素
0 in a1 // => true:a1索引0处也有一个元素
0 in a2 // => false:a2索引0处没有元素
实际将上几行代码复制到浏览器(IE8和谷歌[版本 33.0.1750.146 m])中运行,第三行和第四行输出的结果都是false,也就说在对象直接量中省略值(使用了连续的逗号)时是会创建稀疏数组的。而不是像书中写的"当在数组直接量中省略值时不会创建稀疏数组"。
不知道是原著有误,还是翻译有误,还是不同浏览器所导致的错误。个人理解应该没有错误,因为实际运行的结果证明了书上所写是错误的。ps:望高人指点。
我们在实际开发中很少使用和碰见稀疏数组,但是了解稀疏数组是了解JavaScript数组的真实本质的一部分。这有利于我们更好的了解JavaScript数组。
如果我们确实碰见了稀疏数组,你的代码很可能像对待非稀疏数组一样来对待它们,只不过返回值是undefined。
7.4数组长度
每个数组都有个一个length属性,就是这个属性使其区别于常规的javaScript对象。针对稠密数组(也就是非稀疏数组),length属性值代表数组中元素的个数。其值比数组中最大的索引大1。
当数组是稀疏数组时,length属性的值大于数组中元素的个数。
数组长度总是保证大于它的每一个元素的索引值。或者换一种说法,在数组中(无论是稀疏或非稀疏)找不到一个元素的索引值大于或等于
数组长度。为了维持次规则不变,数组有两个特殊的行为。
第一个特殊的行为:如果为一个数组元素赋值,它的索引i大于或等于现有数组的长度时,length属性的值将设置为i+1。
第二个特殊的行为:如果设置length属性为一个小于当前长度的非负整数n时,当前数组中那些索引值小于或等于n的元素将从数组中删除。事例如下:
var a = [1,2,3,4,5];
a.length = 3;
console.log(a.toString());//=><span style="box-sizing: border-box; color: rgb(34, 34, 34); font-family: Consolas, 'Lucida Console', monospace; white-space: pre-wrap;">1,2,3</span><span style="color: rgb(34, 34, 34); font-family: Consolas, 'Lucida Console', monospace; white-space: pre-wrap;"> </span>
a.length = 1;
console.log(a.toString());//=>1
也可以将length属性值设置为大于当前数组长度。这时的数组是一个稀疏数组。
7.5数组元素的添加和删除
添加数组元素有以下几种常见方式:
方式一:为新索引复制
var a = [];
a[0] = "zero";
方式二:使用push()方法在数组尾部增加一个或多个元素
var a = [];
a.push(''zero');
a.push("one","two");
在数组的尾部添加一个元素与个a[a.length]赋值是一样的。
方式三:使用unshift()方法在数组的首部插入一个元素,并将其它元素依次移到高索引处。
var a = ["zero","one"];
a.unshift("new_zero");
console.log(a.toString());//=> <span style="color: rgb(34, 34, 34); font-family: Consolas, 'Lucida Console', monospace; white-space: pre-wrap;">new_zero,zero,one</span>
删除数组元素有以下几种方式:
方式一: 使用delete运算符来删除数组元素
var a =[1,2,3];
delete a[1];
console.log(1 in a);//=>false
console.log(a.length);//=>3 //注意使用delete运算符删除数组元素不会改变length属性,这是会产生稀疏数组
方式二: 使用pop()方法删除并返回数组中的最后一个元素(一般情况下和push()一起使用),数组length数组减1
var a = [1,2,3];
console.log(a.pop());//=>3
console.log(a.length);//=>2
方式三:使用shift()方法删除并返数组中的第一个元素(一般情况下和unshift()一起使用),数组length属性减1
var a = [1,2,3];
console.log(a.shift());//=>1
console.log(a.length);//=>2
7.6数组的遍历
使用for循环是遍历数组元素最常见的方法:var o = {x:"x",y:"y"};
var keys = Object.keys(o);
var values = [];
for(var i=0;i<keys.length;i++){
var key = keys[i];
values[i] = o[key];
}
console.log(values.toString());//=> x,y
在嵌套循环中和其他性能重要的上下文中,可以看到这种基本的数组遍历需要优化,数组的长度应该只查询一次而非每次都要查询:
for(var i=0;l=keys.length;i<l;i++){
//循环体内容不变
}
假设数组都是稠密的,并且所有元素都是合法的。否则,使用数组元素之前应该先检测他们。比如想要排除null、undefined、和不存在的元素。代码如下:
for(var i=0;i<a.length;i++){
if(!a[i]) continue;// 跳过null、undefined和不存在的元素
}
for(var i=0;i<a.length;i++){
if(a[i] === undefined) continue;//跳过undefined和不存在的值
}
for(var i=0;i<a.length;i++){
if(!(i in a)) continue;//跳过不存在的值
}
还可以使用for/in循环处理稀疏数组。循环每次将一个可枚举的属性名赋给循环变量。不存在的将不会遍历到:
for(var index in sparseArray){
var value = sparseArray[index];
}
for/in循环可以枚举继承的属性名。如果不想用for/in 循环枚举继承的属性,需要使用一些额外的检测方法过滤掉继承属。检测方法如下:
<pre name="code" class="javascript">for(var i in a){
if(a.hasOwnProperty(i)) continue;//跳过继承属性
}
for/in循环不仅可以枚举索引属性,也可以枚举对象属性。以下代码通过检测过滤了数组对象的对象属性:
var a = ["zero","one","two"];
a["p"] ="属性p的值";
for(var i in a){
//跳过不是非负整数的i(也就是跳过对象属性)
if(String(Math.floor(Math.abs(Number(i)))) !== i) continue;
console.log(a[i]);//"zero","one","two" //注意这里没有输出属性p的值
}
ECMAScript 5 定义了一些遍历数组元素的新方法。这些方法中最常用的就是forEach()方法。
var data = ["one","two","three"];
data.forEach(function(value, index, t){//value:数组中的元素 index:数组索引 t:数组本身
console.log(value, index, t);
});
forEach()和相关的遍历方法使得数组本身拥有简单而强大的函数式编程风格。
7.7 多维数组
JavaScript不支持真的多维数组,但可以使用数组的数组来近似。访问数组中数组的元素,只要简单的使用两个[]操作即可。
这里有个例子,它使用二维数组作为九九乘法表:
//创建一个多维数组
var table = new Array(10);//表格有10行
for(var i=0;i<table.length;i++)
table[i] = new Array(10);//表格有10列
//初始化数组
for(var row=0;row<table.length;row++){
for(var col=0;col<table[row].length;col++){
table[row][col] = row*col;
}
}
//使用多维数组来计算(查询) 5*7
console.log(table[5][7]);
7.8数组方法
ECMAScript 3在Array.prototype中定义了一些很有用的操作数组的函数,这意味着所有数组对象都能使用这些函数。
省略对数组方法的介绍。想了解的话,去网上查,或者去看 JavaScript权威指南 第六版。