本章では幾つかのシンプルな問題の解決方法を紹介します。途中、新しいタイプの値、配列とオブジェクト、について説明し、それらに関するテクニックを紹介します。
次のような状況を考えてください: あなたには変わり者の叔母 Emily がいて、彼女には 50 匹以上 (誰も数えたことはありません) の猫を飼っているという噂があります。その叔母が定期的に近況を知らせてきます。通常次のようなものです:
可愛い甥へ、
あなたがスカイダイビングをやっていると聞きましたが、本当ですか? 気をつけて下さいヨ。私の夫がどうなったか憶えているでしょう? 2階から落ちただけですヨ!
こちらはとても刺激的です。Mr. Drake といって素敵な紳士が隣りに越してきました。私は今週ずっと彼の気を引こうとして色々試していました。でも彼は猫が苦手のようです。猫アレルギーでもあるのかしら? 次に合ったら Fat Igor を彼の肩に乗せてみようと思っています。どうなるか興味津々です。
この前話した詐欺事件は思ったよりも好転しています。既に 5 件の '支払い' を済ませて、残るはあと 1 件です。でも少し後悔し始めています。お前の言うとおり、やはりあれは悪いことなのでしょう。
(... 中略 ...)
愛をこめて、叔母 Emily
死亡 27/04/2006: Black Leclère
誕生 05/04/2006: 母 Lady Penelope の子供、Red Lion、Doctor Hobbles 3世、Little Iroquois
あなたはこの老婦人に調子を合わせ、猫の系譜を辿ってみたいと思います。そこでメールの最後に "追伸: Doctor Hobbles 2 世は子供の誕生を喜んだことと思います!" とか、なるべく死んだ猫には触れずに " Lady Penelope はお元気ですか? 彼女はもう 50 歳になるのでしょう?" などと付け加えるのも良いでしょう。あなたは叔母からの古い Eメールをたくさん所有しています。そしてそれらの最後には必ず猫の死と生の情報が同じフォーマットで記されています。
あなたはこれら全てのメールを一通づつ調べたいとは思いません。幸い、我々は例題を必要としていました。そこで我々に代わってこの仕事を行うプログラムを作ってみましょう。差し当たり、最後の Eメールの時点で生存している猫のリストを作るプログラムを書きます。
文通が始まった頃、叔母 Emily は 1 匹の猫 Spot しか飼っていませんでした。 (彼女はその頃はまだ変わり者ではなかったのです。)
プログラムを書き始める前に、そのプログラムにさせることについて何か手掛かりになるものが必要です。次のようなプランで進めましょう:
1. まず "Spot" を含む名前を集めます。
2. 全ての Eメールを年代順に並べます。
3. "誕生" または "死亡" で始まる段落を探します。
4. "誕生" で始まる段落にある名前を集めた名前に加えます。
5. "死亡" で始まる段落にある名前を集めた名前から削除します。
次のようにして段落から名前を抜き取ります:
1. 段落中のコロンを探します。
2. コロンの後の部分を抜き出します。
3. コンマを探し、抜き出した部分を名前に分けます。
叔母 Emily がいつも同じフォーマットを使ったか、名前の綴りに間違いはなかったか、という疑問が残るかもしれません。
まず、属性について話しましょう。JavaScript の値の多くはそれに関連した別の値を持ちます。この関連付けを属性と呼びます。全ての文字列には length と呼ばれる属性があり、文字列の文字数に言及しています。
属性には 2 通りの方法でアクセスすることができます:
var text = "purple haze";
show(text["length"]);
show(text.length);
2 番目の方法は最初の方法の省略表現で、属性名が有効な変数名の時 ― スペースやシンボルを含まず 2 進数字で始まらない時 ― のみ機能します。
数字、ブール、null、および undefined は属性を持ちません。これらの値から属性を読み込もうとするとエラーになります。ブラウザーがどんなエラー・メッセージを出すか知りたければ (ブラウザーによっては暗号のような場合もあります) 次のコードを試してください。
var nothing = null;
show(nothing.length);
文字列値の属性を変えることはできません。これから length 以外にもたくさん出て来ますが、追加も削除も一切許されません。
これらは type オブジェクトの値とは異なります。type オブジェクトの値の主な役割は他の値を保持することです。言ってみれば、属性という形で触手を持っているようなものです。この場合、修正も追加も削除も自由です。
オブジェクトは次のように書くことができます:
var cat = {colour: "grey", name: "Spot", size: 46};
cat.size = 47;
show(cat.size);
delete cat.size;
show(cat.size);
show(cat);
変数のように、オブジェクトに付属している各属性には文字列でラベルが付けられます。最初のステートメントでオブジェクトが作成され、その属性 "colour" は文字列 "grey" を持ち、属性 "name" は文字列 "Spot" に付属し、属性 "size" は数字 46 を参照します。2 番目のステートメントは引数を修正するのと同じ方法で属性 size に新しい値を与えます。
キーワード delete は属性を切断します。存在しない属性を読もうとすると値 undefined が返されます。
まだ存在しない属性が = 演算子で設定されると、そのオブジェクトに付属させられます。
var empty = {};
empty.notReally = 1000;
show(empty.notReally);
名前が有効でない引数名を持つ属性は引用符が付けられ、各括弧で囲まれます:
var thing = {"gabba gabba": "hey", "5": 10};
show(thing["5"]);
thing["5"] = 20;
show(thing[2 + 3]);
delete thing["gabba gabba"];
お分かりのように、角括弧内はどんな表現も可能です。それは文字列に変換されて属性名になります。したがって、属性名を付けるのに変数を使うこともできます:
var propertyName = "length";
var text = "mainline";
show(text[propertyName]);
演算子 in を使ってオブジェクトに属性があるかをテストすることができます。ブール値が生成されます。
var chineseBox = {};
chineseBox.content = chineseBox;
show("content" in chineseBox);
show("content" in chineseBox.content);
コンソールにオブジェクトの値が表示されると、それをクリックして属性を調べることができます。クリックすると出力ウィンドウが 'inspect' ウィンドウに変わります。右上隅の小文字の 'x' は出力ウィンドウに戻る時に使います。左矢印は前の属性に戻る時に使います。
show(chineseBox);
Ex. 4.1
猫の問題に対する解決策は名前の 'セット' にあります。このセットは値の集まりで、どの値もただ 1 度だけ現れます。これらの名前が文字列とするなら、名前のセットを表すオブジェクトを使えると思いますか?
名前をこのセットに追加する方法、削除する方法、セットの中に名前があるかをチェックする方法を考えてください。
これはセットのコンテンツをオブジェクトの属性として保存することで可能になります。名前を追加する時は、その名前の属性を値にセットします。どんな値でもかまいません。名前を削除する時は、この属性を削除します。in 演算子を使うと、名前がそのセット 1 に含まれているかが分かります。
var set = {"Spot": true};
// Add "White Fang" to the set
set["White Fang"] = true;
// Remove "Spot"
delete set["Spot"];
// See if "Asoka" is in the set
show("Asoka" in set);
オブジェクトの値は、一見したところ、変わる可能性があります。値のタイプは、2 章 で述べたように不変で、これらのタイプの既存値を変えることはできません。それらを結合したり、それらから新しい値を抽出することはできますが、特定の文字列値を取る際、その中のテキストを変えることはできません。一方オブジェクトに関しては、属性を変えることによって値のコンテンツを修正することができます。
2 つの数字 120 と 120 がある時、それらは事実上同一と考えられます。オブジェクトの関しては、同じオブジェクトに対する 2 つの異なる参照を持つこと、同じ属性を持つ 2 つの異なるオブジェクトを持つこととの間には差があります。次のコードを見てください:
var object1 = {value: 10};
var object2 = object1;
var object3 = {value: 10};
show(object1 == object2);
show(object1 == object3);
object1.value = 15;
show(object2.value);
show(object3.value);
object1 と object2 は 同じ 値を持つ変数です。実際のオブジェクトは 1 つだけで、これが object1 を変えると object2 の値も変わる理由です。別のオブジェクトを指す変数 object3 は、最初は object1 と同じ属性を持ちますが、異なる経過を辿ります。
JavaScript の == 演算子はオブジェクトを比較し、値が全く同じ場合に true を返すにすぎません。同じコンテンツを持っていてもオブジェクトが異なれば false を返します。これは有益な場合もありますが、実用的ではありません。