こちらの記事は以下の書籍を参考に執筆しました
#Array.fromを使って配列を作成
数値の平均を求める関数お作成しているとして、以下のような関数の定義はうまく実装されません。
function avg() {
const sum = arguments.reduce(function(a, b) {
return a + b;
})
return sum / arguments.length;
}
なぜならargumentsオブジェクトは配列ではなく、配列のようなオブジェクトだからです。。
"arguments.reduce is not a function"
というエラーは、argumentsオブジェクトを配列に変換する必要があるということです。
配列のようなオブジェクトを配列に変換するにはArray.prototype
のslice
メソッドを適応させます。
sliceを引数無しで呼び出すと、配列のシャローコピーを作成します。これを配列のようなオブジェクトに適応しても、配列が作成されます。
またArray.prototype.apply
でもうまくいきます。
配列のようなオブジェクト
配列でないものの、lengthプロパティを持ち、添字によるアクセスができ、forループで処理もできるオブジェクトのこと。
Array.prototype.slice.call(arrayLikeObject);
//または
[].slice.call(arrayLikeObject);
これを使用して先の関数を修正します。
function avg() {
const args=[].slice.apply(arguments);
const sum = args.reduce(function(a, b) {
return a + b;
})
return sum / arguments.length;
}
以下はArray.fromを使用してバージョンです。
function avg() {
const args=Array.from(arguments);
const sum = args.reduce(function(a, b) {
return a + b;
})
return sum / arguments.length;
}
Array.fromは配列のようなオブジェクトを配列に変換します。
##Array.from
とdocument.querySelectorAll
との組み合わせ
document.querySelectorAllのオブジェクト型はNodeList
であり、配列ではありません。
Array.fromはlengthプロパティを持つオブジェクトであれば、どのオブジェクトでもうまくいきます。
配列にしたい対象がlengthプロパティを持ってさえいれば配列に変換できるということです。
Array.from({length:50})
これによりnew Array(50)
と同じ新しい配列が作成されます。
これは長さが50の配列です。
#Array.ofを使って配列を作成
以下のコードを執行するとどうなるでしょう。
let a=new Array(1,2,3)
let b=new Array(1,2)
let c=new Array(1)
aとbは普通の配列が作成されるが、cの結果はempty
となり、1つの値を持った配列を作成できません。
これはArrayのコンストラクタの振る舞いです。
これを回避する方法としてArray.ofファクトリメソッドを使用します。
let a=Array.of(1,2,3)
let b=Array.of(1,2)
let c=Array.of(1)
こうすることで1つの値を持つ配列を作成できます。
**「そんなことしなくても配列リテラルをつ開けばよいのでは?」**と思うかもしれないです。
let a=[1,2,3]
let b=[1,2]
let c=[1]
しかしこれがうまく行かない状況があります。
その一つは配列のサブクラスを使用しているときです。
AwesomeArrayというArrayのサブクラスを使用しているとします。
するとAwesomeArrayはnew AwesomeArray(1)が使えません。
更に配列リテラルも使用できません。
配列リテラルを使用するとAwesomeArrayではなくArrayのインスタンスになってしまうからです。
しかしArray.ofは使用できるため、1つの値に1つだけ含まれたAwesomeArrayのインスタンスが返されます。
#Array.prototype.fillを使って配列を作成
ここでは三目並べゲームを作っていきます。
具体的な構成は以下です。
- 9個のマスは長さ9の配列を使用
- マスにおけるのは"x"と"o"、マスは空("")で表現
配列をboardとするとこのように初期化できる。
const board = new Array(9).map(function(i) {
return ' ';
})
map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map
new Array(9)で9この値を持つ配列を初期化すると、すべての値がundefinedになるので、mapでundefined値をそれぞれスペースに変換します。
しかしこの方法はうまくいきません。
new Array(9)を実行すると実際には9個のundefinedが追加されるのではなく、lengthプロパティに9が設定された新しい配列が作成されるだけだからです。
どういうことかというと、例えば['a','b','c']という配列を作成したとすると内部ではこうなっています。
{
length:3,
0:'a',
1:'b',
2:'c',
}
new Array(9)を実行すると配列の内部では下記のようになっていると思ってしまいます。
{
length:9,
0:undefined,
1:undefined,
2:undefined,
3:undefined,
4:undefined,
5:undefined,
6:undefined,
7:undefined,
8:undefined,
}
しかし実際にはこうです。
{
length:9
}
lengthプロパティが設定されているだけで、値は含まれていません。
欠けている値は穴(hole)と呼ばれます。
mapには対応しないのはこれが原因です。
配列に特定の値を設定するにはfillを使用します。
const board = new Array(9).fill(' ');
#Array.prototype.includeを使って配列を検索する
String.prototype.includeと同様に配列にもincludeが同じように機能します。
Array.prototype.includeメソッドは配列のいずれかのインデックス位置に含まれている値が、指定された値を同じかどうかをチェックします。
これまではindexOfが使用されていました。
indexOfは結果に-1が含まれていた場合の比較を忘れてしまうバグが有りましたが、includeはブーリアンを返します。