一个简单的定义,如果一个对象有
length
属性值,则它就是类数组
那常见的类数组有哪些呢?
这在 DOM 中甚为常见,如各种元素检索 API 返回的都是类数组,如
document.getElementsByTagName
,
document.querySelectorAll
等等。除了 DOM API 中,常见的
function
中的
arguments
也是类数组
那如何把类数组转化为数组呢?这是类数组操作时一个典型的场景,也是一个典型的面试题
以下我们将以
{ length: 3 }
来指代类数组,来作为演示
节选自
日文
【Q168】在 js 中如何把类数组转化为数组
。另外这里有更多的
前端面试题
,欢迎交流
ES6
中有现成的 API:
Array.from
,极为简单
Array.from({ length: 3 })
复制代码
除了
Array.from
还有更简单的运算符
...
扩展运算符,不过它只能作用于
iterable
对象,即拥有
Symbol(Symbol.iterator)
属性值
拥有
Symbol(Symbol.iterator)
属性值,意味着可以使用
for of
来循环迭代
[...document.querySelectorAll('div')]
复制代码
但是严格意义上来说,它不能把类数组转化为数组,如
{ length: 3 }
。它将会抛出异常
// Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
[...{length: 3}]
复制代码
ES5
在此之前,我们先不使用
{ 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(100).map(x => 1) 结果是多少
稀疏数组 (sparse array)
使用 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]
我是山月,可以加我微信 shanyue94
与我交流,备注交流。另外可以关注我的公众号【全栈成长之路】
阳光是sunny