首先,什么是类数组(Array Like)?前端
一个简单的定义,若是一个对象有 length
属性值,则它就是类数组git
那常见的类数组有哪些呢?es6
这在 DOM 中甚为常见,如各类元素检索 API 返回的都是类数组,如 document.getElementsByTagName
,document.querySelectorAll
等等。除了 DOM API 中,常见的 function
中的 arguments
也是类数组github
那如何把类数组转化为数组呢?这是类数组操做时一个典型的场景,也是一个典型的面试题面试
如下咱们将以 { length: 3 }
来指代类数组,来做为演示数组
节选自 日文 【Q168】在 js 中如何把类数组转化为数组。另外这里有更多的 前端面试题,欢迎交流app
ES6
中有现成的 API:Array.from
,极为简单this
// [undefined, undefined, undefined] Array.from({ length: 3 })
除了 Array.from
还有更简单的运算符 ...
扩展运算符,不过它只能做用于 iterable
对象,即拥有 Symbol(Symbol.iterator)
属性值es5
拥有 Symbol(Symbol.iterator)
属性值,意味着可使用 for of
来循环迭代spa
// 适用于 iterable 对象 [...document.querySelectorAll('div')]
可是严格意义上来讲,它不能把类数组转化为数组,如 { length: 3 }
。它将会抛出异常
// Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator)) [...{length: 3}]
在此以前,咱们先不使用 { length: 3 }
,使用如下数据来表明类数组
const arrayLike = { 0: 3, 1: 4, 2: 5, length: 3 }
在 ES5
中能够借用 Array API
经过 call/apply
改变 this
或者 arguments
来完成转化。
最多见的转换是 Array.prototype.slice
Array.prototype.slice.call(arrayLike)
固然因为借用 Array API
,一切以数组为输入,并以数组为输出的 API 均可以来作数组转换,如
Array
(借用 arguments)Array.prototype.concat
(借用 arguments)Array.prototype.slice
(借用 this)Array.prototype.map
(借用 this)Array.prototype.filter
(借用 this)Array.apply(null, arrayLike) Array.prototype.concat.apply([], arrayLike) Array.prototype.slice.call(arrayLike) Array.prototype.map.call(arrayLike, x => x) Array.prototype.filter.call(arrayLike, x => 1)
此时一切正常,可是忘了一个特例,稀疏数组。在此以前,先作一个题,如下代码输出多少
// 该代码输出多少 Array(100).map(x => 1)
使用 Array(n)
将会建立一个稀疏数组,为了节省空间,稀疏数组内含非真实元素,在控制台上将以 empty
显示,以下所示
[,,,]
与 Array(3)
都将返回稀疏数组
> [,,,] [empty × 3] > Array(3) [empty × 3]
当类数组为 { length: 3 }
时,一切将类数组作为 this
的方法将都返回稀疏数组,而将类数组作为 arguments
的方法将都返回密集数组
由上总结,把类数组转化成数组最靠谱的方式是如下三个
Array.from(arrayLike) Array.apply(null, arrayLike) Array.prototype.concat.apply([], arrayLike)
如下几种方法须要考虑稀疏数组的转化
Array.prototype.filter.call(divs, x => 1) Array.prototype.map.call(arrayLike, x => x) Array.prototype.filter.call(arrayLike, x => 1)
如下方法要注意是不是 iterable object
[...arrayLike]