【JavaScript】 Array.fromの使い方を理解する
更新日:2021/06/24
JavaScriptのArray.fromは、使いこなすと便利なメソッドです。
しかし少しわかりにくい面があると思います。
そこで今回は、Array.fromについて少し噛み砕いて解説します。
配列から新しい配列を生成
Array.fromは、配列の要素を一つずつコールバック関数に渡して、コールバック関数の戻り値から新しい配列を作成します。
例えば次のコードは、配列 [ 1 , 2 , 3 ] の要素を順番に、コールバック関数(2番目の引数)に渡して、その結果を新しい配列に格納して、返しています。
const array = Array.from( [ 1 , 2 , 3 ] , e => e * 2 );
console.log( array ); // [ 2, 4, 6 ]
これは、次のようなArrayオブジェクトのmapメソッドでも同じ結果になります。
console.log( [ 1 , 2 , 3 ].map( e => e * 2 ) ); // [ 2, 4, 6 ]
配列の実体を操作するという意味では、mapメソッドの方が分かりやすいです。
しかし、Array.fromはアレイライク(配列のような)オブジェクトや、反復可能オブジェクトも処理の対象にできます。
アレイライクオブジェクトについては、次のページを読んでみてください。
■【JavaScript】 アレイライク・配列風・配列のようなオブジェクトとは
const arrayLike = { length:3 , 0 : 1 , 1 : 2 , 2 : 3 };
const array = Array.from( arrayLike , e => e * 2 );
console.log( array ); // [ 2, 4, 6 ]
アレイライクオブジェクトは、インデックスプロパティがなくても問題ありません。
そのため、次のように要素数だけを指定することも可能です。
const arrayLike = { length:3 };
const array = Array.from( arrayLike , e => 100 );
console.log( array ); // [ 100, 100, 100 ]
上のコードで、引数eとして渡されるのはundefinedです。
そのため、関数内では引数を使用していません。
- 特定の要素数の配列を作成したい。
- 各要素に何らかの処理を加えたい。
この両方の目的に、上のコードが利用できます。
コールバック関数の引数
Array.fromの2番目の引数は、コールバック関数です。
コールバック関数は、次のような2つの引数を受け取ります。
第一引数: 要素値
第二引数: 要素のインデックス
Arrayオブジェクトのmapメソッドは、第三引数として処理中の配列を受け取ります。
コールバック関数の例
const array = Array.from( [ 1 , 2 , 3 ] ,
function( e , index ) { return e * index; }
);
console.log( array ); // [ 0, 2, 6 ]
コールバック関数の例(アロー関数)
const array = Array.from( [ 1 , 2 , 3 ] ,
( e , index ) => e * index
);
console.log( array ); // [ 0, 2, 6 ]
第一引数のみの動作
第一引数のみを指定してArray.fromを実行すると、要素の値をそのまま格納した配列が作成されます。
const array = Array.from( [ 1 , 2 , 3 ] );
console.log( array ); // [ 1 , 2 , 3 ]
作成された配列は、元の配列とは別のものです。
そのため、コピー目的で使用されます。
またアレイライクオブジェクトを配列に変換する目的でも使用できます。
次のコードは、DOMのgetElementsByClassNameで取得したHTMLCollection(アレイライクなオブジェクト)を配列に変換しています。
HTMLCollectionを配列に変換
const elements = document.getElementsByClassName( "classname" );
const elementsArray = Array.from( elements ) ;
elementsArray.forEach( e=>console.log( e ) );
このコードのelementsオブジェクト(HTMLCollection)は、次のようなプロパティを持っています。
0: 要素1
1: 要素2
2: 要素3
・・・
length : 要素数
}
これがそのまま配列に置き換わったイメージです。
アレイライクなオブジェクトに整数インデックスがない場合は、undefinedがセットされます。
次のようにlengthプロパティのみを指定すると、undefinedを要素に持つ配列が作成されます。
const arrayLike = { length:3 };
const array = Array.from( arrayLike );
console.log( array ); // [ undefined, undefined, undefined ]
Array.fromとプリミティブ値
第一引数にUndefinedまたはNullを指定すると、TypeError例外がスローされます。
それ以外のプリミティブ値は、対応するオブジェクトに変換されます。
しかし、変換されたオブジェクトのほとんどは、lengthプロパティを持っていません。
そのため、要素数0の配列が生成されます。
console.log( Array.from( 1 ) ); // [ ]
console.log( Array.from( false ) ); // [ ]
console.log( Array.from( Symbol() ) ); // [ ]
ただし、Stringオブジェクトはlengthプロパティを持っています。
具体的には次のように、文字ごとに分割した数値プロパティと、文字数がセットされたlengthプロパティです。
console.log( new String( "abc" ) );
// { 0: "a" , 1: "b" , 2: "c" , length: 3 }
これは典型的なアレイライクオブジェクトのため、Array.fromに文字列を渡すと、文字ごとに分割した配列が生成されます。
console.log( Array.from( "abc" ) ); // [ "a", "b", "c" ]
console.log( Array.from( "あいうえお" ) ); // [ "あ", "い", "う", "え", "お" ]
ここで、Stringオブジェクトについて問題が浮上してきます。
JavaScriptで使用している文字コードは、2つの要素で一つの文字を表しているものがあります。
例えば、次のような絵文字です。
"🙁🙂"
これをStringオブジェクトに変更してみます。
console.log( new String( "🙁🙂" ) );
// { 0: "\ud83d", 1: "\ude41" , 2: "\ud83d" , 3: "\ude42" , length: 4 }
2文字のはずが、4つに分割されてしまいました。
ということは、Array.fromを実行すると、次のような配列が生成されるはずです。
["\ud83d", "\ude41" , "\ud83d" , "\ude42" ]
実際にやってみます。
console.log( Array.from( "🙁🙂" ) ); // [ "🙁", "🙂" ]
文字単位で分割されました。
なぜでしょうか。
実はStringオブジェクトはイテレーターが実装されていて、このイテレーターは文字単位で値を取得できます。
const iterator = "ab🙁🙂"[Symbol.iterator]();
let iteratorValue = iterator.next();
while (!iteratorValue.done) {
console.log( iteratorValue.value );
// 1回目: a
// 2回目: b
// 3回目: 🙁
// 4回目: 🙂
iteratorValue = iterator.next();
}
【JavaScript】 アレイライク・配列風・配列のようなオブジェクトとはで触れていますが、Array.fromは、オブジェクトにイテレーターが実装されている場合は、イテレーターから値を取得します。
そのため、イテレーターから取得した値で配列が作成されたのです。
Array.fromとthis値
Array.fromの3つめの引数は、コールバック関数に渡すthis値を指定します。
指定しない場合は、this値にundefinedがセットされます。
具体的な例を見ていきましょう。
const array = Array.from( [ 1 , 2 , 3 ] ,
function(e){ return e * this.num; } ,
{ num:100 }
);
console.log( array ); // [ 100, 200, 300 ]
3番目の引数で、numプロパティを持つオブジェクトを指定しています。
このプロパティは、コールバック関数内で this.num として参照できます。
ここでの注意点は、アロー関数が使えないということです。
const array = Array.from( [ 1 , 2 , 3 ] ,
e => e * this.num ,
{ num:100 }
);
console.log( array ); // [ NaN, NaN, NaN ]
アロー関数はthis値を持っていません。
そのため外部のthisの影響を受けます。
今回は、this.num が参照できずundefinedだったために、e * this.num の結果が NaN になりました。
3番目の引数でthis値を指定するなら、アロー関数の使用を避けてください。
アロー関数については、次のページを読んでみてください。
■【JavaScript】 アロー関数は何者!?かっこいいだけじゃない!
更新日:2021/06/24
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。