ECMAScript 6のArrayに関わる構文を試す

[ECMAScript 6 (ECMAScript 2015)](https://developer.mozilla.org/ja/docs/Web/JavaScript/ECMAScript_6_support_in_Mozilla)の`Array`クラスに関わる新しい構文を、いくつかみつくろってご紹介します。網羅的な記事を読んでも、具体的な使い方が思いつかないということはありがちです。そこで、個人的な興味から試してみたコードをご説明してみます。

#Array.from()メソッド
静的メソッド[`Array.from()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/from)は、配列型(array-like)あるいは反復可能(iterable)オブジェクトから新しい`Array`インスタンスをつくります。文字列も反復可能なオブジェクトのひとつです。

```js
console.log(Array.from('文字列も反復可能'));
// ["文", "字", "列", "も", "反", "復", "可", "能"]
```

そうすると、こんな使い方が思い浮かびます。実用性についての疑問は受けつけません。[`String.prototype.split()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/split)と違って、絵文字は正しく扱えます(「[文字列を1文字ずつ配列化（サロゲートペアを考慮）](https://qiita.com/sounisi5011/items/aa2d747322aad4850fe7)」参照)。

```js
console.log(Array.from('どうしてなんだよおおぉお！！！😫').join('゛'));
// ど゛う゛し゛て゛な゛ん゛だ゛よ゛お゛お゛ぉ゛お゛！゛！゛！゛😫
console.log('どうしてなんだよおおぉお！！！😫'.split('').join('゛'));
// ど゛う゛し゛て゛な゛ん゛だ゛よ゛お゛お゛ぉ゛お゛！゛！゛！゛�゛�
```

役に立ちそうなところでは、DOMのノードが配列のような`NodeList`オブジェクトです。

```html
<ul id="items">
	<li>項目1</li>
	<li>項目2</li>
	<li>項目3</li>
</ul>
```

もっとも、[`NodeList.prototype.forEach()`メソッド](https://developer.mozilla.org/docs/Web/API/NodeList/forEach)が使えますので、配列に変換しなくても\<li>要素のテキストを取り出すことはできます。引数には[アロー関数式`=>`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/arrow_functions)を用いました。

```js
const itemsList = document.querySelectorAll('#items li');
itemsList.forEach((item) =>
    console.log(item.textContent)
);
```
```
// コンソール出力
項目1
項目2
項目3
```

けれど、`NodeList`オブジェクトに配列特有のメソッド、たとえば[`Array.prototype.map()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map)は使えません。そして幸いなことに、`Array.from()`メソッドには、第2引数でマップする関数が与えられます。すると、DOMノードに手を加えて、新たな配列がつくれるということです。

```js
const itemsList = document.querySelectorAll('#items li');
const items = Array.from(itemsList,
    (elemennt) => elemennt.textContent
);
console.log(items);    
// ["項目1", "項目2", "項目3"]
```

`Array`インタンスに`Array.from()`メソッドを用いることも考えられます。たとえば、要素数を決めて規則的な配列をつくることです。

```js
const numbers = Array.from(new Array(5), (element, index) => index);
console.log(numbers);  // [0, 1, 2, 3, 4]
```

つぎのように、`Array.prototype.map()`メソッドを使ってもよいのでは、と考える人もいるでしょう。けれど、配列に要素が加わりません。このメソッドは、[値が(undefinedにしても)代入されていない要素については、コールバックを呼び出さない](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Description)からです。

```js
console.log(new Array(5).map((element, index) => index));  // [ , , , , ]
```

# Array.prototype.keys()メソッドとスプレッド構文を使う
配列のインデックスは[`Array.prototype.keys()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/keys)メソッドで`Array Iterator`オブジェクトに取り出すことができます。それを[スプレッド構文](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax)で配列リテラルに渡せば、連番配列がつくれます。スプレッド演算子(`...`)は、Iteratorオブジェクトをカンマ(,)区切りの引数として渡せるのです。

```js
[...Array(5).keys()]  // [0, 1, 2, 3, 4]
```

#Array.prototype.entries()メソッドとfor...of文
[`Array.prototype.entries()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/entries)は、`Array Iterator`オブジェクトを返します。このオブジェクトからは、つぎのように`next()`メソッドと`value`プロパティで、もとの配列要素のインデックスと値の組が新たな配列として順に取り出せるのです(「[反復処理プロトコル](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols)」参照)。

```js
const entries = ['a', 'b', 'c'].entries();
console.log(entries.next().value);  // [0, "a"]
console.log(entries.next().value);  // [1, "b"]
console.log(entries.next().value);  // [2, "c"]
```

オブジェクトが含んでいる要素の配列をまとめて確かめるには、`Array.from()`メソッドが使えます。

```js
console.log(Array.from(['a', 'b', 'c'].entries()));
// [[0, "a"], [1, "b"], [2, "c"]]
```

`Array.prototype.entries()`メソッドで得たオブジェクトは[`for...of`文](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for...of)とともに用いることで、配列要素のデータを順に取り出せます。`let`宣言する変数を配列のように角かっこ`[]`でくくるのは[分割代入](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)の構文です。複数の変数に、配列要素の値をまとめて代入できるのです。

```js
const list = ['a', 'b', 'c'];
for (let [index, element] of list.entries()) {
    console.log(index, element);
}
```
```
// コンソール出力
0 "a"
1 "b"
2 "c"
```

インデックスのみ、あるいは値のみの`Array Iterator`オブジェクトを得る[`Array.prototype.keys()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/keys)および[`Array.prototype.values()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/values)というメソッドもあります。

#配列を検索する
配列から条件に合う最初の値を探すのが、[`Array.prototype.find()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/find)です。引数の関数に要素の値を順に渡して、戻り値が`true`となる値を返します。要素がないときの戻り値は`undefined`です。

```js
const lastWeekOfMarch =
    Array.from(new Array(7),
    (elemen, index) => new Date(2018, 2, 31 - index));
const premiumFri = lastWeekOfMarch.find((date) => date.getDay() === 5);
console.log(premiumFri);  // Fri Mar 30 2018 00:00:00 GMT+0900 (JST)
```

[`Array.prototype.findIndex()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)は、条件に合う最初の要素のインデックスを返します。引数に渡す関数の定め方は、`Array.prototype.find()`と同じです。要素がなければ-1が戻り値となります。

```js
const iroha = Array.from('いろはにほへとちりぬるを');
const index = iroha.findIndex((char) => char === 'へ');
console.log(index);  // 5
```

うえの例では、[`Array.prototype.indexOf()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)でも足りるでしょう。さらに、[`String.prototype.indexOf()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf)の方が手っ取り早いといえます。けれど、引数が関数ですので、より複雑な条件が定められるのです。

つぎのコードは、配列要素から値`NaN`を探して、インデックスを得ます。`NaN`かどうかは、静的メソッド[`Number.isNaN()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN)で調べていることにご注意ください。これまでグローバル関数[`isNaN()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/isNaN)がよく用いられました。けれども、この関数は引数を数値に変換しようとするため、数値にできない値(たとえば`undefined`)や文字列(たとえば'NaN')には`true`を返してしまいます。新たに備わった`Number.isNaN()`メソッドは、`NaN`を正しく評価するのです。

```js
const irregular = [undefined, null, Infinity, NaN];
const index = irregular.findIndex((value) => Number.isNaN(value));
console.log(index);  // 3
```

もうひとつ新しい静的メソッド[`Object.is()`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/is)は、引数に渡したふたつの値が同じかどうかブール(論理)値で返します。`NaN`だけでなく他の値の比較にも使えるので、こちらも覚えておくとよいでしょう。

#Array.prototype.fill()メソッド
配列の長さを決めて、値はすべて同じにしたいとき`Array.from()`メソッドでつくれます。

```js
const values = Array.from(new Array(5), (element) => 0);
```

けれど、要素を同じ値で埋めるなら、[`Array.prototype.fill()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/fill)の方が手軽です。

```js
const values = new Array(5).fill(0);
console.log(values);  // [0, 0, 0, 0, 0]
```

また、メソッドの第2および第3引数で、書き替える要素の始まりと終わりが決められます。ただし、終わりはインデックスより1大きい数値とすることにご注意ください。

```js
values.fill(1, 1, values.length - 1);
console.log(values);  // [0, 1, 1, 1, 0]
```

#Arrayクラスを継承する
ECMAScript 6では、ビルトインクラスが継承できるようになりました。そこで、`Array`のサブクラスを定めてみます。[`class`宣言](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/class)で名前を決めたあと、[`extends`キーワード](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/extends)に添えるのが基本クラスです。コンストラクタは[`constructor()`メソッド]
(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/constructor)で定義します。

配列のコンストラクタは、引数の数が決まりません。このようなときに用いるのが、[残余引数](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions_and_function_scope/rest_parameters)(`...`)です。渡された引数は、まとめて配列として受け取られます。受け取った引数は、[`super`キーワード](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/super)で親クラスのコンストラクタに渡さなければなりません。このときも、残余引数と同じ構文(`...`)の[スプレッド演算子](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_operator)を使えば、今度は配列をカンマ(`,`)区切りの引数として渡すことができるのです。

```js
class ArrayEx extends Array {
	constructor(...args) {
		super(...args);
	}
}
```

実は、クラス定義には[デフォルトで加えられるコンストラクタ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/constructor#既定のコンストラクタ)があって、他のクラスを継承した派生クラスではまさにうえのかたちなのです。したがって、この場合`constructor()`メソッドは省いても構いません。

`extends`キーワードは、スーパークラスの静的メソッドも継承します。そして、たとえば継承された`Array.from()`メソッドは、サブクラスのインスタンスを返すのです。

```js
class ArrayEx extends Array {
	/* constructor(...args) {
		super(...args);
	} */
}
const myArray = ArrayEx.from([0, 1, 2]);
console.log(myArray);  // [0, 1, 2]
console.log(myArray instanceof ArrayEx);  // true
```

#Array.of()メソッド
静的メソッド[`Array.of()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/of)は、任意の数の引数がそのまま要素に納められた配列をつくります。`Array()`コンストラクタや、もっと簡単に配列リテラルを用いれば済みそうに感じるかもしれません。おそらく、サブクラスで整数ひとつのインスタンスをつくるときに使うのでしょう。

```js
console.log(new ArrayEx(3));  //  [ , , ]
console.log(ArrayEx.of(3));  //  [3]
```


**参考**:「[ECMAScript 6’s new array methods](http://2ality.com/2014/05/es6-array-methods.html)」