你真的會將類數組轉化爲數組嗎

首先,什麼是類數組(Array Like)?前端

一個簡單的定義,若是一個對象有 length 屬性值,則它就是類數組git

那常見的類數組有哪些呢?github

這在 DOM 中甚爲常見,如各類元素檢索 API 返回的都是類數組,如 document.getElementsByTagNamedocument.querySelectorAll 等等。除了 DOM API 中,常見的 function 中的 arguments 也是類數組面試

那如何把類數組轉化爲數組呢?這是類數組操做時一個典型的場景,也是一個典型的面試題前端工程化

如下咱們將以 { length: 3 } 來指代類數組,來做爲演示數組

節選自 日文 【Q168】在 js 中如何把類數組轉化爲數組。另外這裏有更多的 前端面試題,歡迎交流服務器

ES6+

ES6 中有現成的 API:Array.from,極爲簡單微信

// [undefined, undefined, undefined]
Array.from({ length: 3 })
複製代碼

除了 Array.from 還有更簡單的運算符 ... 擴展運算符,不過它只能做用於 iterable 對象,即擁有 Symbol(Symbol.iterator) 屬性值app

擁有 Symbol(Symbol.iterator) 屬性值,意味着可使用 for of 來循環迭代運維

// 適用於 iterable 對象
[...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 與我交流,備註交流。另外能夠關注個人公衆號【全棧成長之路】

若是你對全棧面試,前端工程化,graphql,devops,我的服務器運維以及微服務感興趣的話,能夠關注我
相關文章
相關標籤/搜索