文章简介
本文为【JavaScript 漫游】专栏的第 006 篇文章,记录笔者在了解 JS 数据类型 array 中摘录的知识点。
- 数组的本质是对象
- 属组的
length
属性 for ... in
循环和数组的遍历- 数组的空位
- 类数组对象
除了上述 5 个重要知识点,学习数组更为重要的是掌握它的静态方法和实例方法,笔者计划再后面单独写一篇文章进行总结。
数组本质上是一种特殊的对象
typeof
运算符返回数据的数据类型是 object
,这说明在本质上,数组属于一种特殊的对象。而它的特殊性体现在,它的键名是按次序排列的一组整数。
var arr = ['a', 'b', 'c'];
Object.keys(arr); // ['0', '1', '2']
length 属性
数组的 length
属性,返回数组的成员数量。
['a', 'b', 'c'].length; // 3
只要是数组,就一定有 length
属性。该属性是一个动态的值,等于键名中的最大整数加上 1
。要注意的是,数组的数字键不需要连续,length
属性的值总是比最大的那个整数键大 1
。
var arr = ['a', 'b'];
arr.length // 2
arr[2] = 'c';
arr.length // 3
arr[9] = 'd';
arr.length // 10
arr[1000] = 'e';
arr.length // 1001
length
属性是可写的,如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到 length
设置的值。如果将 length
属性设为 0
,就可以清空数组。如果设的值不合法,比如 -1,JS 会报错。
var arr = [ 'a', 'b', 'c' ];
arr.length // 3
arr.length = 2;
arr // ["a", "b"]
arr.length = 0;
arr // []
for...in
循环和数组的遍历
既然数组是一种特殊的对象,那么 in
运算符和for...in
循环同样能作用到数组上。
in
运算符检查某个键名是否存在。
var arr = [];
arr[100] = 'hello';
1 in arr; // false
100 in arr; // true
for...in
循环可以遍历数组的所有键名。特别注意的是,它不仅会遍历数组的数组键,也会遍历非数组键。终究是因为数组本质是一种对象,它的标准键名是数值,但如果人为赋予字符串键名是没有问题的。
var arr = ['a', 'b', 'c'];
arr['hello'] = 'world';
arr.length; // 3
for (var key in arr) {
console.log(key);
}
// 0
// 1
// 2
// hello
基于 for...in
循环会遍历到非数组键,所以,遍历数组的时候最好不要用for...in
,转而使用for
循环或while
循环更好。
笔者认为,遍历对象使用
Object.keys
方法,遍历数组使用for
循环以及数组的实例方法forEach
是最好的,工作遍历数组时使用forEach
方法比for
循环的频率更高。
数组的空位
当数组的某个位置是空元素,就称该数组存在空位。
var arr = [1, , 1];
a.length; // 3
a[1]; // undefined
数组的某个位置是空位,与某个位置是 undefined
,是不一样的。如果是空位,使用数组的 forEach
方法、for...in
结构、以及Object.keys
方法进行遍历,空位都会被跳过。而由于空位的存在对 length
属性没有影响,使用 length
属性配合 for
循环遍历数组的时候,要确保数组没有空位。
var arr = [1, , 1];
arr.forEach(function(value, index) {
console.log('arr[' + index + '] = ' + value);
})
// arr[0] = 1
// arr[2] = 1
for (var key in arr) {
console.log('arr[' + key + '] = ' + arr[key]);
}
// arr[0] = 1
// arr[2] = 1
Object.keys(arr); // ['0', '2']
for (var i = 0; i < arr.length; i++) {
console.log('arr[' + i + '] = ' + arr[i]);
}
// arr[0] = 1
// arr[1] = undefined,值为 undefined 在实际开发过程会诱发报错
// arr[2] = 1
var arr = [1, undefined, 1];
arr.forEach(function(value, index) {
console.log('arr[' + index + '] = ' + value);
})
// arr[0] = 1
// arr[1] = undefined
// arr[2] = 1
for (var key in arr) {
console.log('arr[' + key + '] = ' + arr[key]);
}
// arr[0] = 1
// arr[1] = undefined
// arr[2] = 1
Object.keys(arr); // ['0', '1', '2']
for (var i = 0; i < arr.length; i++) {
console.log('arr[' + i + '] = ' + arr[i]);
}
// arr[0] = 1
// arr[1] = undefined
// arr[2] = 1
由此可知,遍历数组的最好方法就是 forEach
。
简单总结,空位就是数组没有这个元素,所以不会被遍历到,而 undefined
表示数组有这个元素,值是 undefined
,所以遍历不会跳过。
类数组对象
如果一个对象的所有键名都是正整数或零,并且有 length
属性,那么这个对象就很像数组,语法上称为 类数组对象。
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
obj[0] // 'a'
obj[1] // 'b'
obj.length // 3
obj.push('d') // TypeError: obj.push is not a function
类数组对象并不是数组,因为它们不具备数组特有的方法,它们的根本特征是有 length
属性,只要有 length
属性,就可以认为这个对象类似于数组。但是有一个问题,这种 length
属性不是动态值,不会随着成员的变化而变化。
var obj = {
length: 0
};
obj[3] = 'd';
obj.length // 0
典型的类数组对象是函数的 arguments
对象,以及大多数 DOM 运算集,还有字符串。
数组的 slice
方法可以将类数组对象变成真正的数组。
var str = 'hello';
var arr = Array.prototype.slice.call(str);
除了转为真正的数组,类数组对象还有一个办法可以使用数组的方法,就是通过 call
方法将数组的方法放到对象上。
function print(value, index) {
console.log(index + ' : ' + value);
}
var str = 'hello';
Array.prototype.forEach.call(str, print);
要注意的是,使用 call
方法调用 forEach
这种方式,比直接使用数组原生的 forEach
方法要慢,所以最好先将类数组对象转为真正的数组,然后再直接调用数组的 forEach
方法。