This translation is incomplete. Please help translate this article from English.
あらゆるプログラミング言語は、それぞれ異なったデータ構造を持っています。この記事では、JavaScript で使用可能な組み込みデータ構造の一覧と、他のデータ構造の構築にも使えるように、それらがどのような性質を持ち合わせているかについて述べることにします。また可能である場合は、他のプログラミング言語におけるデータ構造との対比も行います。
ECMAScript 標準仕様では 6 つのデータ型が定義されています。
- Number
- String
- Boolean
- Null
- Undefined
- Object
以下の章では、 これらの型がどのようにデータを表現するのか、また、どのように組み合わせて複雑なデータ構造を作るのかを見ていくことにします。
プリミティブ値
object 型をのぞく全ての型は不変 (immutable) な値として定義されています。特筆すべき点としては、 string 型が(Cなどとは違って)不変な値である点です。これらの型の値を、プリミティブ値 (primitive value) と呼びます。詳細は Strings の節で触れることとします。
Boolean 型、 Null 型 および Undefined 型
これらの型では、true 、 false 、 null 、 undefined の 4 つの定数があります。これらは定数であるため、リッチなデータ(およびデータ構造)の表現はできません。
Number 型
ECMAScript 標準仕様によると、この型は「倍精度 64 ビット形式による IEEE 754 値
」となります。特筆すべきは整数値であるとは指定されていないという点です。さらに言うならば、この型は浮動小数点数値にもなりますし、+Infinity 、 -Infinity 、 NaN (not-a-number) といった記号的な値も持ち合わせています。
数値はたいてい数値としてのみ表されるものの、JavaScript はいくつかのバイナリ演算を提供しています。そのため、ビットマスクを用いて、一つの数値で複数の真偽値を表現することも可能です。しかしながら、JavaScript が(真偽値の配列や真偽値のプロパティを複数持つオブジェクトのような)真偽値の集合を表現する手段を提供していることや、ビットマスクはコードの可読性、わかりやすさ、保守性を大きく損なってしまうことから、この機能は大抵、バッドプラクティスとして考えられています。 ローカルストレージの容量的制約への対処や、ビット単位での転送量を考える必要のある極限状態など、非常に特殊なケースにおいてはこうしたテクニックが必要となるでしょう。 このテクニックは、あくまでも最適化が必要な場合の最終手段としてのみ考慮すべきです。
String 型
C のような言語とは異なり、JavaScript の文字列は 不変 (immutable) です。これは一度生成した文字列は変更が不可能であるということを意味しています。しかしながら、オリジナルの文字列を操作することで別の文字列を生成することは可能となっています。例としては(個別に文字を抜き出す、または String.substr() を用いることによる)オリジナルの部分文字列の切り出しや、連結演算子( + )または String.concat() を用いた、2 つの文字列の連結などが挙げられます。
「文字列より」のコードに注意!
文字列を用いて複雑なデータ構造を表現したいと思うこともあるでしょう。文字列は以下のような使い勝手の良さを備えています。
- 文字列連結によって複雑な文字列の構築が簡単である
- 文字列はデバッグが簡単である(出力される情報は常に文字列の一種である)
- 文字列は(インプットフィールド、 ローカルストレージの値、
XMLHttpRequestのresponseTextなど)数多くの API において基準として扱われており、全てを文字列で処理したいと思わせる
理屈の上では、文字列を用いることで、あらゆるデータ型の表現が可能ではあります。ですが、あまり良い考えとはいえません。例として、セパレータ文字を使用することでリストの模倣が可能です(役割としては JavaScript の配列の方がよっぽど適していますが)が、残念なことにセパレータ文字が「リスト」の 要素となってしまった場合、そのリストは破綻します。エスケープした文字を使用することでこの問題に対処することは可能ですが、そのルールをすべてに用意する必要がある上、用途に応じて適切な道具を用いなかったことによるメンテナンスコストの増大を招きます。
文字列型の使用はテキストデータや記号データを表すのには向いていますが、別のデータ構造を表す為に文字列のパースや正しい抽象化を用いる必要がある場合には向いていないと言えるでしょう。
Object
JavaScript において、オブジェクトはプロパティのバッグと見なせます。オブジェクトリテラル構文では、設定されたプロパティの初期化に制約が存在しています。プロパティは追加および削除が可能です。プロパティの値は、他のオブジェクトを含むあらゆる型の値が設定可能であり、これにより複雑なデータ構造の構築が可能となっています。
「通常の」オブジェクトおよび関数
Javascript オブジェクトはキーと値を所持しています。キーは文字列ですが、値はなんでもかまいません。これにより、オブジェクトはハッシュマップの様相を呈します。しかしながら、オブジェクトは非標準の [object Object] 疑似プロパティを保有していることに注意しなくてはなりません。このプロパティがサポートされている環境では、'__proto__' という名前のプロパティの操作は許可されておらず、'__proto__' プロパティはそのオブジェクトのプロトタイプを表しているのです。(入力フィールドのような)どんな文字列が当てはめられるのかが未知のコンテクストでは注意が必要です。これによって実際に起こった問題にリンクを張っておきます。この問題では、適切な StringMap の使用が解決のための代替案となります。
Array(配列)
Array は整数値をキーにするプロパティと length プロパティの間に特殊な関係の存在する、標準オブジェクトです。加えて述べるなら、配列は、indexOf(配列中の値の探索)や push (配列への要素の追加)などといった、配列を操作するのに便利ないくつかのメソッドを提供する Array.prototype を継承しています。これにより配列はリストまたはセットとして表現するために必要な機能を備えることとなります。
Date
日時についての表現を考慮するとき、組み込みの Date オブジェクトのユーティリティメンバを用いるのが最も妥当でしょう。
WeakMap, Map, Set
非標準です。 ECMAScript 6 での標準化が期待されています。
これらのデータ構造は、キーとしてオブジェクトへの参照を持ちます。WeakMap および Map がオブジェクトを値と関連づける一方で、Set はオブジェクトのセットを表します。Map と WeakMap の相違点としては、前者がキーであるオブジェクトの列挙が可能であるのに対し、後者がガベージコレクションに最適化されている点です。
Map と Set は純粋に ECMAScript 5 での実装も可能ではありますが、(”完全”な意味での)オブジェクトの比較は不可能であり、キーとしたオブジェクトの線形探索が必要なことによるパフォーマンス上の問題があります。(WeakMap を含む)Map と Set のネイティブな実装では対数を用いることにより概して定数時間でのキーおよび関連する値の探索が可能となっています。
従来ならば、DOM ノードにデータを結びつける場合は、オブジェクトに直接プロパティを設定するか、data-* 属性を用いるのが普通でした。これらの手法は同じコンテクストで実行されるあらゆるスクリプトからデータの利用が可能であるため、不都合な面を持ち合わせていました。 Map および WeakMap を使用することにより、オブジェクトへのプライベートなデータの結びつけを簡単に行うことができます。
TypedArray(型付き配列)
非標準です。ECMAScript 6 での標準化が期待されています。