1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

配列のようなオブジェクト、Array-like object とは

Posted at

はじめに

JavaScriptには、配列ではないが、配列に似たオブジェクトであるArray-like object と呼ばれるオブジェクトがあります。
配列のように扱えるが、配列の機能が全て使用できるわけではない、この配列のようなオブジェクトについてみていきたいと思います。

Array-like object、配列のようなオブジェクトとは

Array-like object とは、以下の配列の特徴をもつが、配列ではないオブジェクトのことです。

  • lengthプロパティをもつ
  • 0から始まる数値インデックスによる要素へのアクセスが可能

Array-like object は配列ではないため、mapfilterといった配列メソッドを直接呼び出すことができません。

querySelectorAll()

Array-like object を返す関数として、querySelectorAll()が挙げられます。

querySelectorAll()は引数に渡したセレクタに合致したNodeListを返しますが、このNodeListは配列ではなくArray-like object です1
NodeListからは、mapfilterを直接呼び出すことができませんが、forEachについては実装されており、呼び出すことができます。

<body>
    <div class="foo">a</div>
    <div class="foo">b</div>
    <div class="foo">c</div>
</body>
const nodeList = document.querySelectorAll('.foo')

console.log(nodeList)
console.log
NodeList(3) [div.foo, div.foo, div.foo]
0: div.foo
1: div.foo
2: div.foo
length: 3
[[Prototype]]: NodeList

prototypeArray.prototypeではなく、NodeList.prototypeとなっていることがわかります。
NodeList.prototypeには以下のようなプロパティが含まれています。

[[Prototype]]: NodeList
entries: ƒ entries()
forEach: ƒ forEach()
item: ƒ item()
keys: ƒ keys()
length: (...)
values: ƒ values()
constructor: ƒ NodeList()
Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.toStringTag): "NodeList"
get length: ƒ length()
[[Prototype]]: Object

確かにforEachが存在しています。
mapfilterは見当たらず、使用しようとするとエラーとなります。

文字列

文字列はプリミティブな値ですが、Array-like な振る舞いをします。
lengthプロパティをもち、数値インデックスによって文字へアクセスが可能なためです。

また、文字列に対してもforEachmapfilterなどを呼び出すことはできません。

配列メソッドを使うには

Array.from()を使う方法

Array.from()はArray-like object や iterble object を配列にすることができます2
イテレート可能でないオブジェクトを配列に変換するためには、lengthプロパティを持つ必要があります3

先の例でのnodeListArray.from()の引数として渡すと、Array.prototypeをもつ配列が返されます。

const nodeList = document.querySelectorAll('.foo')

const nodeListArray = Array.from(nodeList)

console.log(nodeListArray)

(3) [div.foo, div.foo, div.foo]
0: div.foo
1: div.foo
2: div.foo
length: 3
[[Prototype]]: Array(0)

NodeListや文字列に対してmapfilterを使いたい場合には、配列へと変換すればいいわけですね。

Function.call()で呼び出す方法

Function.call()はオブジェクトのメソッドを他のオブジェクトに割り当てて使用できるようにするメソッドです。
Function.call()を使用すれば、配列に変換せずに、Array.prototypeから間接的にmapfilterを呼び出すことも可能です。

第1引数にはthisとして使われるオブジェクトを指定し、第2引数以降には呼び出す関数への引数を指定します。
Array.prototype.filterを使いたいときには、第1引数にArray-like object、第2引数にfilter関数への引数を渡して使用します。

const foo = {}

for(let i=0; i<3; i++){
    foo[i] = i;
}

foo.length = 3;

const bar = Array.prototype.filter.call(foo, (fooProperty)=>fooProperty >= 1)

console.log(bar)
[1, 2]

この例ではfooというArray-like object にfilterを間接的に呼び出して適用しています。
Array-like object にfilterを適用したことで、1以上の要素のみに絞り込まれた配列が返ってきていることがわかります。

  1. querySelectorAll()から返されるNodeListは、DOM要素が変化してもNodeListへの変化はない、静的なNodeListと呼ばれるものです。反対に、DOMが更新されるとリストも自動的に更新されるものを生きたNodeListと呼びます。

  2. シャローコピーによって引数を複製して配列に変換するため、Array-like object 内の要素がオブジェクトの場合などには、変換後の配列の要素も同じ参照をもつことになるため注意が必要です。

  3. lengthも持たない場合には、空配列[]が返されます。

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?