数字为特性的数组数据类型


数组是内存中线性的连续数据,在 JavaScript 中,并没有实际的数组,而是以对象来模拟出相似的操作外观。如果你要在 JavaScript 中创建所谓的数组(以下还是先简称数组),可以使用Array构造出实例。例如:

> var array1 = new Array();
undefined
> array1.length;
0
> var array2 = new Array(10);
undefined
> array2.length;
10
> var array3 = new Array(10, 20, 30);
undefined
> array3.length;
3
>

上面示范了三种Array实例构造的方式,第一种方式构造出没有任何元素的数组,第二种方式构造出长度为 10 的数组,每个索引位置(0 到 9)都是undefined,第三种方式则构造出内含三个元素的数组,索引 0 开始分别是 10、20、30。

实际上,很少人会直接使用Array构造数组,而会使用数组字面量(Array literal)。例如:

> var array1 = [];
undefined
> array1.length;
0
> var array2 = [];
undefined
> array2.length = 10;
10
> array2.length;
10
> var array3 = [10, 20, 30];
undefined
> array3.length;
3
>

你并没有看错,在 JavaScript 中,Array创建的实例,length特性是可读可写的。例如:

function print(array) {
    for(var i = 0; i < array.length; i++) {
         console.log(array[i]);
    }
}

var array = [1, 2, 3];
print(array);  // 1 2 3
console.log('........');

array.length = 5;
print(array);  // 1 2 3 undefined undefined
console.log('........');

array.length = 2;
print(array);  // 1 2
console.log('........');

array.length = 3;
print(array);  // 1 2 undefined

在上面的例子中,数组原本的长度为 3,后来设定length为 5,索引 3 与 4 的部份会是空项目(empty item),指定索引取值的话会是undefined,将length设为 2 时,原本索引 2 的数据就没了,就算设回 3 也找不回来了。

虽然数组中的空项目取值的话会是undefined,不过,当数组中有空项目时,运算或函数会有不一致的行为:

> 0 in [1,, 3];
true
> 0 in [,,];
false
> 0 in [undefined, undefined, undefined];
true
> [undefined, undefined].forEach(function(elem) {
...     console.log(undefined);
... });
undefined
undefined
undefined
> [,,].forEach(function(elem) {
...     console.log(undefined);
... });
undefined
>

从上面的例子来看,数组中的空项目与undefined并不同,可以的话,应避免创建有空项目的数组。

数组是内存中线性的连续数据,在 JavaScript 中,并没有实际的数组,而是以对象来模拟出相似的操作外观,事实上,用Array构造出的对象,索引其实就是以代表数字的特性罢了,事实上索引指定方式也可以用字符串,只要它代表数字:

> var array = [1, 2, 3];
undefined
> array['0'];
1
> array['1'];
2
> array['2'];
3
> for(var i in array) {
...     console.log(i);
... }
0
1
2
undefined
> delete array[1];
true
> array;
[ 1, <1 empty item>, 3 ]
>

也因此,因为索引其实就是数组对象上的特性,你也可以用delete删除数组中的元素,也因此,你可以轻易地使用普通对象,来模拟出数组的操作外观:

function print(array) {
    for(var i = 0; i < array.length; i++) {
         console.log(array[i]);
    }
}

// 显示 100 200 300
print({  
    '0' : 100,
    '1' : 200,
    '2' : 300,
    length : 3
});

在 JavaScript 中如上模拟出所谓的数组,是非常普遍的应用。那么,构造Array实例,或者说使用数组字面量构造数组的好处是什么?当然是为了拥有Array已定义的行为,例如自动依元素内容来调整length

> var array = [];
undefined
> array.length;
0
> array[0] = 100;
100
> array.length;
1
> array[10] = 900;
900
> array.length;
11
>

数组的长度随时可以增加或减少,指定索引元素时也不一定要连续指定,例如上例中,直接指定了索引 10 为 900,其它未指定的2到9索引处,全都是undefined(就像没有 2 到 9 的特性名称罢了),在 JavaScript 中,也没有所谓数组超出索引的问题,例如上例若指定array[10000],充其量就是返回undefined

直接使用Array实例来进行数组操作,还可以获得Array上已定义的方法。例如排序与迭代:

var names = ["Justin", "Monica", "Irene"];

names.sort(function(n1, n2) {
         return n1.length - n2.length;
      })
      .forEach(function(elem) { // Irene Justin Monica
          console.log(elem);
      });

forEach是 ECMAScript 5 规范中为Array新增的方法,可以指定函数,实际上,只要是类数组的对象,也可以使用forEach。例如:

var obj = {  
   '0' : 100,
   '1' : 200,
   '2' : 300,
   length : 3
};

obj.forEach = Array.prototype.forEach;

obj.forEach(function(elem, index, arr) {
    console.log(elem);
});

obj.forEach(console.log);

上面范例的执行结果是:

100
200
300
100 0 { '0': 100,
  '1': 200,
  '2': 300,
  length: 3,
  forEach: [Function: forEach] }
200 1 { '0': 100,
  '1': 200,
  '2': 300,
  length: 3,
  forEach: [Function: forEach] }
300 2 { '0': 100,
  '1': 200,
  '2': 300,
  length: 3,
  forEach: [Function: forEach] }

数组中每个元素会作为实参传入函数,这是 JavaScript 风格的遍历数组方式,ECMAScript 5 规范的Array中,有很多这类接受函数的方法,像是everysome,可以测试数组元素是否全部符合,以及某个符合条件:

function isLength5(value, index, array) {
    return value.length === 5;
}

function lengthLessThan6(value, index, array) {
    return value.length < 6;
}

var names = ["Justin", "Monica", "Irene"];

console.log(names.every(isLength5));         // false
console.log(names.some(lengthLessThan6));    // true

处理一串数据时常用的filtermapreduce方法当然也有:

var names = ["Justin", "Monica", "Irene"];

var sum = names.filter(function(elem) {
                    return elem.length > 5;
               })
               .map(function(elem) {
                   return elem.length;
               })
               .reduce(function(accum, elem) {
                   return accum + elem;
               }, 0);

console.log(sum);  // 12

reduce是从数组索引 0 迭代至尾端,相对地,有个reduceRight是从数组尾端往前迭代至索引 0。除此之外,ECMAScript 5 还新增了indexOflastIndexOf方法,更多Array上可用的方法,可以参考Array - JavaScript | MDN


展开阅读全文