- 伪数组有时又称类数组,是数组很相似的对象,因此伪数组无法使用数组类型的相关 API(如 push、shift、pop、map 等)
- 若我们想要使用这些 API 的话,那么就需要先将伪数组转为真数组
伪数组(array-like object)
如果一个对象的所有键名都是正整数或零,并且有length
属性,那么这个对象就很像数组,语法上称为“类似数组的对象”(array-like object)。
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
上面代码中,对象obj
就是一个类似数组的对象。但是,“类似数组的对象”并不是数组,因为它们不具备数组特有的方法。对象obj
没有数组的push
方法,使用该方法就会报错。
“类似数组的对象”的根本特征,就是具有length
属性。只要有length
属性,就可以认为这个对象类似于数组。但是有一个问题,这种length
属性不是动态值,不会随着成员的变化而变化。
var obj = {
length: 0
};
obj[3] = 'd';
obj.length // 0
上面代码为对象obj
添加了一个数字键,但是length
属性没变。这就说明了obj
不是数组。
典型的“类似数组的对象”是函数的arguments
对象,以及大多数 DOM 元素集,还有字符串。
// arguments对象
function args() { return arguments }
var arrayLike = args('a', 'b');
arrayLike[0] // 'a'
arrayLike.length // 2
arrayLike instanceof Array // false
// DOM元素集
var elts = document.getElementsByTagName('h3');
elts.length // 3
elts instanceof Array // false
// 字符串
'abc'[1] // 'b'
'abc'.length // 3
'abc' instanceof Array // false
上面代码包含三个例子,它们都不是数组(instanceof
运算符返回false
),但是看上去都非常像数组。
数组的**slice**
方法可以将“类似数组的对象”变成真正的数组。
var arr = Array.prototype.slice.call(arrayLike);
除了转为真正的数组,“类似数组的对象”还有一个办法可以使用数组的方法,就是通过call()
把数组的方法放到对象上面。
function print(value, index) {
console.log(index + ' : ' + value);
}
Array.prototype.forEach.call(arrayLike, print);
上面代码中,arrayLike
代表一个类似数组的对象,本来是不可以使用数组的forEach()
方法的,但是通过call()
,可以把forEach()
嫁接到arrayLike
上面调用。
下面的例子就是通过这种方法,在arguments
对象上面调用forEach
方法。
// forEach 方法
function logArgs() {
Array.prototype.forEach.call(arguments, function (elem, i) {
console.log(i + '. ' + elem);
});
}
// 等同于 for 循环
function logArgs() {
for (var i = 0; i < arguments.length; i++) {
console.log(i + '. ' + arguments[i]);
}
}
字符串也是类似数组的对象,所以也可以用Array.prototype.forEach.call
遍历。
Array.prototype.forEach.call('abc', function (chr) {
console.log(chr);
});
// a
// b
// c
注意,这种方法比直接使用数组原生的forEach
要慢,所以最好还是先将“类似数组的对象”转为真正的数组,然后再直接调用数组的forEach
方法。
var arr = Array.prototype.slice.call('abc');
arr.forEach(function (chr) {
console.log(chr);
});
// a
// b
// c
使用 slice 将伪数组转真数组
var arr1 = [1, 2, 3]
var arr2 = arr1.slice()
var arr3 = arr1
arr2 // [1, 2, 3]
arr1 === arr2 // false
arr1 === arr3 // true
类数组对象是一个拥有 length
属性和索引元素的普通对象。这类对象并没有数组的标准方法,比如 push
,pop
或者 slice
。这种对象的一个例子是 arguments
对象。当你调用 Array.prototype.slice.call(arrayLike)
,你实际上是在调用 slice
函数,并将它的 this
值设置为你的类数组对象。由于 slice
函数只关心对象的 length
属性和索引元素,因此它可以正常工作。
slice
函数的工作原理是创建一个新数组,并将原数组或类数组对象中的元素复制到新数组中,因此返回的结果是一个真正的数组。
因此,这种方法可以将具有 length
属性和索引元素的任何对象转换为真实的数组,然后使用所有的数组方法,如 map
,filter
等,并且这个数组包含了原对象的所有元素。