array-like object っていったい何?iterable との違いは?言語仕様に立ち返って説明する
イントロ
JavaScript では array-like object (配列みたいなオブジェクト) という用語が使われるが、MDN を探してもハッキリとした定義は見当たらない。この記事では ECMAScript の仕様に基づいて、array-like object がどういうものなのかを解説する。
必要な知識: プロパティ、配列、オブジェクト、プロトタイプ、メソッド
よく知られている array-like object の性質
例えば document.getElementsByClassName()
の返り値は HTMLCollection
であり、これは array-like である。HTMLCollection
は .slice()
などの配列メソッドを持っていないが、array-like であるため以下のように強制的に配列メソッドを呼び出すことができる。
// HTMLCollection
const btns = document.getElementsByClassName("btn")
// array
const btnsArray = Array.prototype.slice.call(btns)
.call(btns)
で this
を btns
にすることで、強制的に配列の .slice()
メソッドを btns
に対して呼び出している。仕組みを詳しく知りたい場合は、これらの記事が参考になるだろう。
このテクニックは array-like object を配列に変換するのによく用いられている。この他にも、文字列や arguments
オブジェクトなどが array-like であるとよく言われる。
MDN の説明
Some JavaScript objects, such as the NodeList returned by document.getElementsByTagName() or the arguments object made available within the body of a function, look and behave like arrays on the surface but do not share all of their methods. The arguments object provides a length attribute but does not implement the forEach() method, for example.
(Indexed collections - JavaScript | MDN より引用)
Some JavaScript objects, such as the NodeList returned by document.getElementsByTagName() or the arguments object made available within the body of a function, look and behave like arrays on the surface but do not share all of their methods. The arguments object provides a length attribute but does not implement the forEach() method, for example.
(Indexed collections - JavaScript | MDN より引用)
訳: 「document.getElementsByTagName()
の返り値の NodeList
や、関数内で使える arguments
オブジェクトといった一部のオブジェクトは、表面上では配列のように振る舞うが、すべての配列メソッドを持っているわけではない。」
このように、配列と同じように振る舞う、ということしか書いておらず、正確な定義が分からない。
ECMAScript の仕様での定義
つまり array-like object とは length
プロパティの値が ToLength で長さに変換できる(失敗しない)オブジェクトである。そして、以下のように ToLength は内部的に ToNumber を呼び出しており、この ToNumber が失敗しなければ ToLength も失敗しないということが分かる。
ToNumber は実は JavaScript の Number()
関数によっても使われるものである。詳しく説明しないが new
なしで呼び出されると NewTarget は undefined
になるため、Number()
関数の返り値は ToNumber の結果そのままである。
以上から分かったことは、array-like object = 「Number(obj.length)
で例外が発生しないようなオブジェクト obj
」である。
ほとんどのオブジェクトは array-like ?
ということは、空オブジェクトでさえも Number(obj.length)
は Number(undefined)
つまり NaN
となるため、array-like となってしまうのである。実際、Number()
が例外を投げるケースは、以下のように引数が Symbol
である場合か一部の Object
の場合しかない。
Object
の場合で例外が発生するのは、以下のような特殊なケースである。
Number({ toString: null }) // Uncaught TypeError: Cannot convert object to primitive value
以上を踏まえて例をあげる。
array-like object でない例
{ length: { toString: null } }
{ length: Symbol("mySymbol") }
null
undefined
array-like object の例
{}
{ length: null }
{ length: 7 }
実際、以下のように配列メソッドを呼び出すことができる。
Array.prototype.slice.call({}) // []
Array.prototype.slice.call({length:3}) // [empty, empty, empty]
Note: 1
や true
といった値も「object である」という点以外では array-like object の条件を満たしている。
iterable との違いは?
ここまで読めば、array-like は iterable とは全く異なるインターフェースであることが既に分かるだろう。iterable であるとは、Symbol.iterator
メソッドをもち、それが iterator を返すことである。
const arr = [1, 2, 3]
const iterator = arr[Symbol.iterator]()
iterator は呼び出されるたびに「次の値があるか(done
)、あれば値は何か(value
)」という情報を含むオブジェクトを返すことになっている。
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {done: true}
iterable インターフェースは JavaScript のいろいろなところで使われており、スプレッド構文 [...iterable]
、fun(...iterable)
や for-of for (const item of iterable)
などである。
iterable であるビルトインタイプは Array
、TypedArray
、String
、Map
、Set
のみであり、当然自分で iterable を実装することもできる。
まとめ
仕様に基づけば、{}
のような全く配列らしくない値でさえも array-like であるということが分かった。しかし、一般的には "array-like" は 「length
プロパティが数値でありかつ arrLike[0]
のように要素にアクセスできる」という意味で使われることが多いので、それぞれ広義、狭義として覚えておくとよいだろう。
Author And Source
この問題について(array-like object っていったい何?iterable との違いは?言語仕様に立ち返って説明する), 我々は、より多くの情報をここで見つけました https://qiita.com/righteous/items/56d6086ec83be08d3553著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .