关于类数组和数组

在前端开发中,我们经常会碰到这样一个概念:类数组。那究竟类数组和数组有什么样的关联呢?

类数组(ArrayLike)

从字面上我们就能认识到,类数组是指和数组有相似特点的对象,可以读取类数组的长度,也可以通过索引获取类数组中的元素。大家最为熟知的类数组,就是 arguments 。今天我们来熟悉另外一个类数组对象。

1
2
3
4
var nodeList = document.querySelectorAll('div');
console.log(nodeList[0]);//<div>....</div>
nodeList.pop();//Uncaught TypeError: document.querySelectorAll(...).pop is not a function(…)
console.log(nodeList instanceof Array);// false

从上述代码中我们可以看到,定义了一个nodeList变量,也能通过索引获取相关元素,但当我们执行数组pop方法时,控制台报错了:Uncaught TypeError: document.querySelectorAll(…).pop is not a function(…) 。然后我们通过 instanceof 方法,控制台输出为 false 。这是因为nodeList 是一个类数组对象,但是不能执行数组的 pop 方法。

常见的类数组对象: arguments, nodeList(以下代码形式返回类型为NodeList:childNodes, labels(select元素), getElementsByName(name), getElementsByClassName(className), getElementsByTagName(tagName), getElementsByTagNameNS(namespaceURI, tagName), querySelectorAll(selectors), document.all.tags(tagName)), styleSheetList(document.styleSheets), HTMLCollection(以下代码形式返回类型为HTMLCollection: children, document.images, document.links, document.anchors, document.scripts, document.forms, document.applets, document.embeds等) 等。

类数组拥有一些原生数组(Array)的行为,但类数组相比数组具有更加自由的扩展,它来自于对开发者对Javascript 对象的扩展。简言之:对于它的原型(prototype)我们可以自由定义,而不会污染到javascript原生的Array。类数组来自于数组,但比数组更适合扩展。

类数组转换为数组对象

上面我们说了类数组的优点,但是类数组毕竟不是数组对象,数组对象的大部分方法,类数组都不支持。对于数组(Array)对象,拥有很多方法,诸如: shift, unshift, pop, push, slice, splice, concat等等。ES6
又提供了forEach, isArray, indexOf, lastIndexOf, every, some, map, filter, reduce 等方法。当我们想对类数组对象进行操作时,如果能直接调用数组的方法,将大大提高我们的开发效率,如何将类数组转换为数组呢?
** Array.prototype.slice.call(arrayLike) **

1
Array.prototype.slice.call(ArrayLike);

先执行完上述代码后,类数组就完成了向数组对象的转换。

  • Array.prototype.slice方法返回的是一个Array对象,这也是为什么用slice方法的原因。

  • 能调用call的只有方法,所以我们用Array.prototype.slice.call, 而不能用[].call这种形式。同时call的第一个参数表示调用slice的环境变为了arrayLike对象。call方法也是js中实现继承的一种方式。


update: ES6新增了Array.from()方法

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
console.log(p);
});

// arguments对象
function foo() {
var args = Array.from(arguments);
// ...
}


2016/08/11
ES6的扩展运算符 …

1
2
3
4
5
let x = document.querySelectorAll('div'); 
let y = Array.from(x);
console.log(x instanceof Array);//false
console.log(y instanceof Array);//true
console.log([...x] instanceof Array);//true