- 伪数组有时又称类数组,是数组很相似的对象,因此伪数组无法使用数组类型的相关 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 // 3obj.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 // 2arrayLike instanceof Array // false// DOM元素集var elts = document.getElementsByTagName('h3');elts.length // 3elts 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 = arr1arr2 // [1, 2, 3]arr1 === arr2 // falsearr1 === arr3 // true
类数组对象是一个拥有 length 属性和索引元素的普通对象。这类对象并没有数组的标准方法,比如 push,pop 或者 slice。这种对象的一个例子是 arguments 对象。当你调用 Array.prototype.slice.call(arrayLike),你实际上是在调用 slice 函数,并将它的 this 值设置为你的类数组对象。由于 slice 函数只关心对象的 length 属性和索引元素,因此它可以正常工作。
slice 函数的工作原理是创建一个新数组,并将原数组或类数组对象中的元素复制到新数组中,因此返回的结果是一个真正的数组。
因此,这种方法可以将具有 length 属性和索引元素的任何对象转换为真实的数组,然后使用所有的数组方法,如 map,filter 等,并且这个数组包含了原对象的所有元素。
