配列とカスタムオブジェクト(連想配列)
配列はArrayクラスのインスタンスです。ただし、以下の前提があります。
配列のインデックスは0から連続した自然数である
勘違いしがちなのは、lengthプロパティがオブジェクトのメンバ数を表す、と思ってしまうことです。実際には最大の自然数のインデックス+1の値が格納されます。上の前提により、これが配列の要素数を表すことになります。
var ary = new Array(); ary[-2] = "N"; //インデックスが自然数でないので配列の要素ではない。 ary[3] = "Y"; //配列の要素 ary[3.5] = "N"; //インデックスが自然数でないので配列の要素ではない。 alert(ary.length); //"4"と表示される。 //ary[3]があるとary[0],ary[1],ary[2]はあるとみなされる。 //ary[-2],ary[3.5]は要素としてカウントされない。
つまり、Arrayクラスのオブジェクトは、追加されるメンバのプロパティ名を監視し、それが自然数で、lengthプロパティ以上であった場合、lengthプロパティをその値+1に変更する、という機能を持っています。
var ary = new Array(); ary[2] = "Y"; alert(ary.length); //"3"と表示される。 ary[4] = "Y"; alert(ary.length); //"5"と表示される。 ary[3] = "Y"; alert(ary.length); //"5"と表示される。
配列の各要素を扱うコードは以下のようになります。*1
var ary = new Array(); ary[2] = "二"; ary[4] = "四"; for(var i = 0; i < ary.length; i++){ if(!(i in ary)){ continue; //メンバとして存在しなければ処理せず次の要素へ } alert(ary[i]); //'二', '四'が順に表示される。 }
ここでiの初期値をマイナスにしたり、i++をi+=0.5にしたりするのは適切でない、ということになります。また、ここでfor inループを使うのも適切ではありません。
配列の省略記法として以下があります。
var ary = ["零", "壱", "弐", "参"];
これは以下と同じです。
var ary = new Array(); ary[0] = "零"; ary[1] = "壱"; ary[2] = "弐"; ary[3] = "参";
応用すると、多次元配列も簡単に書けるようになります。
var ary = [0, [10, 11], [[200, 201], [210, 211]]]; alert(ary[2][0][1]); //'201'が表示される
次に、カスタムオブジェクト(連想配列)です。こちらは配列のようにlengthが自動的に更新されるような機能を持ちませんので、オブジェクトのすべてのメンバを処理するためにはfor inループを使います。
var obj = new Object(); obj.zero = "零"; obj.one = "壱"; obj.two = "弐"; obj.three = "参"; for(var i in obj){ alert(i + ":" + obj[i]); //"zero:零","one:壱","two:弐","three:参"が順に表示される }
obj.zeroをobj["zero"]と書いても同じです。違いとして、前者には変数として許容できる名前のみが使用できるのに対し、後者はほぼ何でも設定が可能、ということです。また、変数の値の文字列を使用したい場合にも有効です。
obj["zero"] = "零"; obj[1] = "壱"; //obj.1 = "壱"; →これはだめ。1は変数名にならない。 obj["弐"] = "弐"; var str = "three" obj[str] = "参";
カスタムオブジェクトの省略記法として以下があります。
var obj = {zero:"零", one:"壱", two:"弐", three:"参"};
プロパティ名を"や'でくくれば日本語や空白も設定可能です。配列と同様、入れ子にすることができます。
var obj = {zero:"零", one:{two:"壱弐", three:"壱参"}}; alert(obj.one.two); //"壱弐"と表示される。
*1:要素の存在判定を==nullで行っていたのをinに置き換えました。こちらのほうが妥当だと思います。