Last Updated: 2/6/2024, 5:44:57 AM

# JavaScript の配列の初期化

# はじめに

# ◯ 同じ値で全部初期化したい。

fill を使うのが良さそうです。

Array(5).fill(0)
// [ 0, 0, 0, 0, 0 ] 

# ◯ 連番で初期化したい。

スプレッド構文が一番短そうです。

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

他にも以下の様なやり方があります。

Array.from(Array(5).keys())
Array.from(Array(5), (v, k) => k)
Array.from({length: 5}, (v, k) => k);
// いずれも返り値は
// [0, 1, 2, 3, 4]

上記の記法は全て以下の記事を参考にさせていただきました。

Hello, world!

# ◯ JavaScript のコードの実行の仕方

JavaScript のソースコードは、デベロッパーツールのコンソールにコピペして簡単に実行できます。

# ◯ ハマった話

JavaScript は、このあたりの挙動は、 とりあえず、理解する必要はなく、やり方さえ知っていればいいのかなと感じました。。 Array の初期化まわりは、teratail で質問までして、かなりハマりました。

# 1. Array

[ <5 empty items> ] というのが、くせのある挙動をします。

array = Array(5)
array
> array
[ <5 empty items> ]
> 

ほとんど理解できていません。

array = Array(5)
array[2] = undefined
array
array.length
> array
[ <2 empty items>, undefined, <2 empty items> ]
> array.length
5
>

new をつけてもつけなくても動作は同じです。

Array(5)
new Array(5)
> Array(5)
[ <5 empty items> ]
> new Array(5)
[ <5 empty items> ]
> 

# 2. Array.from()

// JavaScript
Array.from([0, 1, 2, 3, 4])

# Python
[0, 1, 2, 3, 4].copy()

Array.from() (opens new window)
Array.from() メソッドは、  配列風オブジェクト  や反復可能オブジェクトから、 新しい、浅いコピーの Array インスタンスを生成します。

# ◯ 配列風オブジェクト

配列風オブジェクト(array like object) という表現は割とよく見かけます。 本当に配列っぽいオブジェクトです。

Array.from() (opens new window)
Array.from() は、以下のものから Array を生成します。

  •  配列風オブジェクト   (length プロパティおよびインデックス付けされた要素を持つオブジェクト)  もしくは
  • 反復可能オブジェクト (Map や Set のような要素を取得するオブジェクト)

# 3. Array.prototype.fill()

Array.prototype.fill() (opens new window)
fill() メソッドは、 配列中の開始位置から終了位置までの要素を固定値で設定します。 その際、終了位置は含まれません。

# 4. Array.prototype.keys()

// JavaScript
Array(5).keys()

# Python
range(5)

Array.prototype.keys() - MDN web docs (opens new window)
keys() メソッドは、配列の各インデックスのキーを含む 新しい Arrayイテレーター オブジェクトを返します。

動作は以下の様な具合です。

iterator = Array(5).keys();
iterator.next();
iterator.next();
iterator.next();
iterator.next();
> iterator.next();
{ value: 0, done: false }
> iterator.next();
{ value: 1, done: false }
> iterator.next();
{ value: 2, done: false }
> iterator.next();
{ value: 3, done: false }
> iterator.next();
{ value: 4, done: false }
> iterator.next();
{ value: undefined, done: true }
>

以下のページがいくらか参考になりました。

さきほどの Array.from と組み合わせて、 0 から 99 までの配列を作ってみます。

> Array.from(Array(100).keys())
[
   0,  1,  2,  3,  4,  5,  6,  7,  8,
   9, 10, 11, 12, 13, 14, 15, 16, 17,
  18, 19, 20, 21, 22, 23, 24, 25, 26,
  27, 28, 29, 30, 31, 32, 33, 34, 35,
  36, 37, 38, 39, 40, 41, 42, 43, 44,
  45, 46, 47, 48, 49, 50, 51, 52, 53,
  54, 55, 56, 57, 58, 59, 60, 61, 62,
  63, 64, 65, 66, 67, 68, 69, 70, 71,
  72, 73, 74, 75, 76, 77, 78, 79, 80,
  81, 82, 83, 84, 85, 86, 87, 88, 89,
  90, 91, 92, 93, 94, 95, 96, 97, 98,
  99
]
> 

# 5. prototype

# ◯ メソッド

また prototype という文字が見えたと思います。 Python はクラスオブジェクトの属性に関数を代入すれば、それがメソッドになります。 一方で JavaScript ではクラス下にある prototype 属性に、 代入した関数がメソッドになるらしいです。

以下の記事がとてもわかりやすいのです。

# ◯ クラスメソッド

Array.from は Python で言う所のクラスメソッドです。 なぜこのような設計になっているのでしょうか? Python で言う所の from Array import from_ みたいなことはできないのでしょうか? ブラウザに搭載された JavaScript は標準ライブラリを import するということができないので、 このようにして組込型に突っ込む形で、名前空間を分けています。

# 6. スプレッド構文

スプレッド構文 (opens new window)
スプレッド構文を使うと、関数呼び出しでは 0 個以上の引数として、Array リテラルでは 0 個以上の要素として、Object リテラルでは 0 個以上の key-value のペアとして、Array や String などの iterable オブジェクトをその場で展開します。

# 7. MDN Web docs の性格

主に MDN Web docs から引用しました。 MDN Web docs は技術仕様書ではなく、それをわかりやすく噛み砕いたものになるそうです。 正確には ECMAScript が JavaScript の標準規格になるそうです。 それを各社が実装している様な具合でしょうか。

# おわりに

まさか 配列, Array でこんなにハマると思いませんでした。

付録

# 付録

この記事は以下の記事のサブ記事になります。

以下の記事を読んでいて気になったので JavaScript の Array を調べていました。

// JavaScript
// 元のコード
const from0To100Array = Array.from(Array(100).keys());
const isEvenNumber = i => i % 2 === 0;
const addAll = (total, i) => total + i;
const totalOfEvenNumberUnder100 = from0To100Array.filter(isEvenNumber).reduce(addAll);

alert(totalOfEvenNumberUnder100);
// JavaScript
// 少し書き換えてみる。
const n = 100
const array = Array.from(Array(n).keys());
const isEven = i => i % 2 === 0;
const add = (a, b) => a + b;
const sumOfEvenArray = array.filter(isEven).reduce(add);

alert(sumOfEvenArray);
// JavaScript
// さらに換えてみる。
const n = 100
const array = Array.from(Array(n).keys());
const evenArray = array.filter(i => i % 2 === 0)
const sumOfEvenArray = evenArray.reduce((a, b) => a + b);

alert(sumOfEvenArray);

ここから先は、以下の JavaScript のコードの残りのキーワード const, =>, === を追っていきます。

# 1. const

// JavaScript
const i = 0

# Python
#     const は Python には無い

頭の const は定数宣言です。 const が頭につくと変更できない、再代入することができません。 これを「定数」と言います。 「変数」と「定数」、2つを総称してカッコつけてたまに「名前」と呼ばれてたりします。

Python では None, True, Flase が 「定数」 (opens new window) ですが 自分で定数を作ることはできません。 class, def などと同じ「予約語」 (opens new window) らしく 代入しようとすると SyntaxError (opens new window) になります。 SyntaxError なので、例えば関数定義文の中で代入しようとすると、そもそも関数の実行はおろか定義さえできません。

>>> def f():
...     None = 0
... 
  File "<stdin>", line 2
SyntaxError: can't assign to keyword
>>> 

なぜわざわざ Qiita の人が const を書いているかというと 一般に変数に再代入しない方が、副作用がない方が 可読性のよいコードになると言われているからです。

Haskell などの純粋な関数型言語では副作用を認めてくれません。 そういう言語らしいです。 そのため for 文が使えません。

for i in range(10):
    print(i)
    # なぜなら i に再代入してしまうから
    # 再代入は副作用であり、純粋な関数型言語では禁止されている。

そのため関数型言語では、map, filter, reduce そして再帰呼び出しを駆使して、 for 文を書き換えないといけません。

# 2. アロー関数 =>

// JavaScript
i => i % 2 === 0

# Python
lambda i: i % 2 == 0

この矢印はアロー関数と呼ばれるもので Python のラムダ式と等価です。 JavaScript はかなりアロー関数や無名関数が多用されていて辛いです。

最初は何故 Guido が lambda を嫌っていたのかわからなかったのですが JavaScript を触ってると Guido が嫌った理由がわかる気がします。 Guido はやっぱりすごいと思ってしまう訳です。

# 3. 比較演算子 ===

// JavaScript
5 ==  '5'  // true
5 === '5'  // false

# Python
5 == '5'  # False
          # === は Python には無い

また === は厳密等価演算子と呼ばれるもので == とは違います。 JavaScript だけで Python にはありません。

なんでこんなものがあるかというと 恐らく「暗黙の型変換」の設計に失敗したからです。 Python では「暗黙の型変換」について、 とても慎重に議論された形跡が見られます。