JS数组是无类型的,数组元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类型。JS数组的索引是基于0的32位数值:第一个元素的索引值为0,最大可能的索引为4 294 764(2(32) - 2),数组最大能容纳4 294 967 295个元素。JS数组是动态的,根据需要它们会增长或缩减,并且在创建数组时无需声明一个固定的大小或者在数组大小变化时无需重新分配空间。JS数组可能是稀疏的:数组元素的索引不一定要连续的,它们之前可以有空缺。每个JS数组都有一个length属性;针对非稀疏数组,该属性就是数组元素的个数,针对稀疏数组,length比所有元素的索引要大。

数组继承自Array.prototype中的属性,它定义了一套丰富的数组操作方法。


创建数组

数组直接量

例如:

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];  // 3个元素,中间元素为undefined
var undefs = [,,];     // 2个元素,都是undefined


构造函数Array()

var a = new Array();                       // 调用时没有参数
var a = new Array();                       // 指定长度,此时,数组中没有存储值,甚至数组的索引属性0,1等还未定义
var a = new Array(5,4,3,2,"testing");      // 显式指定两个或多个数组元素


数组元素的读写

使用 [] 操作符来访问数组中的一个元素。使用该语法既可以读又可以写数组的一个元素。


稀疏数组

稀疏数组就是包含从0开始的不连续索引的数组。通常,数组的length属性值代表数组中元素的个数。如果数组是稀疏的,length属性值大于元素的个数。

当在数组直接量中省略值时不会创建稀疏数组。省略的元素在数组中是存在的,其值是 undefined。



数组长度

每个数组有一个 length 属性。

当设置 length 属性为一个小于当前长度的非负整数 n 时,当前数组中那些索引值大于或等于n的元素将从中删除。

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


数组元素的添加和删除

添加数组元素最简单的方法,为新索引赋值:

a = [];         // 空数组
a[0] = "zero";  // 添加元素
a[1] = "one";



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

a = [];
a.push("zero");        // 添加一个元素
a.push("one", "two");  // 添加两个元素



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

a = [1,2,3];
delete a[1];
1 in a;       //=> false
a.length      //=>3 delete操作并不会影响数组长度


删除数组元素与为其赋值 undefined 值是类似的。

对一个数组元素使用 delete 不会修改数组的 length 属性,也不会将元素从高索引处一下来填充已删除属性留下的空白。如果从数组中删除一个属性,它就变成稀疏数组。

也可以简答地设置 length 属性为一个新的期望长度来删除数组尾部的元素。数组有 pop()方法,后者一次使减少长度1并返回被删除元素的值。还有一个shift()方法,从数组头部删除一个元素。和delete不同的是shift()方法将所有元素下移到比当前索引低1的地方。

splice()是一个通用的方法来插入,删除或者替换数组元素,它会根据需要修改 length 属性并移动元素到更高或较低的索引处。

详见——数组方法。


数组遍历

使用for循环:

for (var i = 0; i < keys.length; i++){
    ... ...
}



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;           // 跳过不存在的元素,仍然要处理存在的undefined元素
    ... ...
}



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

for (var index in sparseArray){
    var value = sparseArray[index];
}



ECMAScript5定义了一些遍历数组元素的新方法,如forEach()方法:

var data = [1,2,3,4,5];
var sumOfSquares = 0;
data.forEach(function(x){
                       sumOfSquares += x*x;
});



多维数组

JS不支持真正的多维数组,但可以用数组的数组来近似:

var table = new Array(10);
for (var i = 0; i < table.length; i++)
    table[i] = new Array(10);     // 每行10列

for(var row = 0; row < table.length; row++){
    for(col = 0; col <table[row].length;col++){
        table[row][col] = row * col;
    }
}
// 使用多维数组来计算
var product = table[5][7];



数组方法 —— ECMAScript3

join()

Array.join()方法将数组中所有元素都转化为字符串并连接在一起,返回最后生成的字符串。可以指定一个可选的字符串在生成的字符串中来分隔数组的各个元素。如果不指定分隔符,默认使用逗号:

var a =[1,2,3];
a.join();       //=> "1,2,3"
a.join(" ");    //=>"1 2 3"
a.join("");     //=>"123"



reverse()

Array.reverse()方法将数组中的元素颠倒顺序,并返回逆序的数组。


sort()

Array.sort()元素将数组中的元素排序并返回排序后的数组。当不带参数调用sort()时,数组元素以字母表顺序排序,如果数组包含 undefined 元素,它们会被排到数组的尾部。

为了按照其他方式而非字母表顺序进行数组排序,必须给sort()方法传递一个比较函数,该函数决定了它的两个参数在排好序的数组中的先后顺序。假设第一个参数应该在前,比较函数应该返回一个小于0的数值。反之,假设第一个参数应该在后,函数应该返回一个大于0的数值。并且,假设两个值相等,函数应该返回0.


concat()

Array.concat()方法创建并返回一个新数组,它的元素包括调用concat的原始数组的元素和concat的每个参数。

例如:

var a = [1,2,3];
a.concat(4,5);                 // [1,2,3,4,5]
a.concat([4,5]);               // [1,2,3,4,5]
a.concat([4,5], [6,7]);        // [1,2,3,4,5,6,7]
a.concat(4, [5, [6,7]]);       // [1,2,3,4,5,[6,7]]



slice()

Array.slice()方法返回指定数组的一个片段或子数组。参数分别指定了片段的开始和结束位置。返回的数组包含第一个参数指定的位置和所有到但不含第二个参数指定的位置之间的所有数组元素。如果只指定一个参数,返回的数组将包含从开始位置到数组结尾的所有元素。如参数中出现负数,它表示相对于数组中最后一个元素的位置,参数-1指定了最后一个元素。


splice()

Array.splice()方法是在数组中插入或删除元素的通用方法。splice()会修改调用的数组。

例如:

var a = [1,2,3,4,5,6,7,8];
a.splice(4);            // 返回[5,6,7,8],a是[1,2,3,4]
a.splice(1,2);          // 返回[2,3], a是[1,4]
a.splice(1,1);          // 返回[4], a是[1]



splice()的前两个参数指定了需要删除的数组元素,紧随其后的任意个数的参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。


push()和pop()

push()和pop()方法允许将数组当作栈来使用。push()方法在数组的尾部添加一个或多个元素,并返回数组新的长度。pop()方法则相反,它删除数组的最后一个元素,减少数组长度并返回删除的值。这两个方法都修改并替换原始数组而非生成一个修改版的新数组。


unshift()和shift()

unshift()和shift()方法的行为非常类似于push()和pop(),不一样的是前者是在数组的头部而非尾部进行元素的插入和删除操作。unshift()在数组的头部添加一个或多个元素,并将已存在的元素移动到更高索引的位置来获得足够的空间,最后返回数组的新的长度。shift()删除数组的第一个元素并将其返回,然后把所有随后的元素下移一个位置来填补数组头部的空缺。

当使用多个参数调用unshitf()时它的行为令人惊讶。参数是一次插入的,而非一次一个地插入。这意味着最终的数组中插入的元素的顺序和它们在参数列表中的顺序一致。


toString()和toLocaleString()

该方法将其每个元素转换为字符串并且输出用逗号分隔的字符串列表。


数组方法—— ECMAScript5

大多数方法的第一个参数接受一个函数,并且对数组的每个元素调用一次该函数。如果是稀疏数组,对不存在的元素不调用传递的函数。在大多数情况下,调用提供的函数使用三个参数:数组元素,元素的索引和数组本身。通常,只需要第一个参数值,可以忽略后两个参数。


forEach()

forEach()方法从头至尾遍历数组,为每个元素调用指定的函数。然后forEach()使用三个参数调用该函数,数组元素,元素的索引和数组本身。如果只关心数组元素的本身,可以编写只有一个参数的函数——额外的参数将忽略:

例如:

var data = [1,2,3,4,5];
var sum = 0;
data.forEach(function(value){ sum += value;});
data.forEach(function(v, i, a){});



forEach()无法在所有元素都传递给调用的函数之前终止遍历。也就是说,没有像for循环中使用相应的break语句。


map()

map()方法将调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值,例如:

a = [1,2,3];
b = a.map(function(x){return x*x;});  // [1,4,9]


map()返回的新数组,它不修改调用的数组。


filter()

filter()方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的,该函数返回true或false。

例如:

a = [5,4,3,2,1];
smallvalues = a.filter(function(x) { return x < 3;});  // [2,1]
everyother = a.filter(function(x){return i%2;});  // [5,3,1]



filter()返回的数组总是稠密的,会跳过稀疏数组中的缺少的元素。


every()和some()

every()和some()方法是数组的逻辑判定,它们对数组元素应用指定的函数进行判定,返回true或false。

every()方法就像“针对所有”;当且仅当对数组中的所有元素调用函数都返回true,它才返回true。

some()方法就像“存在”,当数组中至少有一个元素调用判定函数返回true,它就返回true;并且当且仅当数值中的所有元素调用判定函数都返回false,它才返回false。

一旦every()和some()确认该返回什么值它们就会停止遍历数组元素。


reduce()和reduceRight()

reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值。称为“注入”和“折叠”。

例如:

var a = [1,2,3,4,5];
var sum = a.reduce(function(x,y){return x+y;},0);  // 求和
var product = a.reduce(function(x,y){return x*y},1);  // 求积
var max = a.reduce(function(x,y){return (x>y)?x:y;})  // 求最大值



reduce()需要两个参数。第一个是执行化简操作的函数,化简操作的任务就是利用某种方法把两个值合成或简化为一个值,并返回简化后的值。第二个可选的参数是一个传递给函数的初始值。

reduce()使用的函数与forEach()和map()使用的函数不同。数组元素,元素的索引和数组本身将作为第2 - 4 个参数传递给函数,第一个参数是到目前为止的化简操作积累的结果。第一个调用函数时,第一个参数是一个初始值,它就是传递给reduce()的第二个参数。在接下来的调用中,这个值就是上一次化简函数的返回值。

reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低处理数组,而不是从低到高。


indexOf()和lastIndexOf()

indexOf()和lastIndexOf()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者没有找到就返回-1.indexOf()从头至尾搜索,而lastIndexOf()则反向搜索。

例如:

a = [0,1,2,1,0];
a.indexOf(1);  // 1
a.lastIndexOf(1);  // 3
a.indexOf(3); // -1



检测数组类型

在ECMAScript5中,可以使用Array.isArray()函数来做这件事情:

Array.isArray([])  // true
Array.isArray({}) // false



作为数组的字符串

在ECMAScript5中,字符串的行为类似于只读的数组。除了使用charAt()方法来访问单个的字符以外,还可以使用方括号:

例如:

var s = "test";
s.charAt(0)  // "t"
s[1]            // "e"



当然,针对字符串的typeof操作符仍然返回 “string",如果给Array.isArray()传递字符串,它将返回false。

字符串的行为类似于数组的事实使得通用的数组方法可以应用到字符串上。

字符串是不可变值,故当把它们作为数组看待时,它们是只读的。如push(),sort(),reverse()和splice()等数组方法会修改数组,它们在字符串上是无效的。不仅如此,使用数组方法来修改字符串会导致错误:出错的时候没有提示。


类数组对象

... ...