Arrayクラスに対する拡張

【抜粋】一部省略
Object.extend(Array.prototype, Enumerable);

Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0; i < this.length; i++)
      iterator(this[i]);
  },
(省略)
  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});

既存Arrayクラスに対してメソッドを追加しています。まずEnumerableクラスをArrayクラスに継承させます。次に_reverseメソッドに既存reverseメソッドを退避しています*1。その後、他のメソッドを追加しています。

_eachメソッド

【抜粋】
  _each: function(iterator) {
    for (var i = 0; i < this.length; i++)
      iterator(this[i]);
  },

_eachメソッドは、継承したEnumerableクラスで抽象メソッド扱いになっているものです。Enumerableクラスを継承した場合は必ず定義しなければなりません。Arrayクラスではインデックス0〜length-1の要素を順に引数で渡された関数iteratorで処理しています。こうすることで、Enumerableクラスから継承したメソッドは配列の各要素を順に処理することができます。

_eachメソッドは外部から使用されることを想定していません。

clearメソッド

【抜粋】
  clear: function() {
    this.length = 0;
    return this;
  },

clearメソッドは配列(this)のlengthを0にして、配列(this)を返却します。lengthを0にするだけでいいんですね・・・。

【例】
var ary = [0,1,2,3].clear();
alert(ary[0]);
//"undefined"と表示される

firstメソッド

【抜粋】
  first: function() {
    return this[0];
  },

最初の要素の値を返却します。例は省略。

lastメソッド

【抜粋】
  last: function() {
    return this[this.length - 1];
  },

最後の要素の値を返却します。length-1は常に最大インデックスとなります。

compactメソッド

  compact: function() {
    return this.select(function(value) {
      return value != undefined || value != null;
    });
  },

Enumerableクラスから継承したselectメソッドにより、値がundefinedでなく、またはnullでない・・・ってあれ? ここは「かつ」(&&)にすべきのような。ただ、undefinedとnullはほぼ同じ扱いなので、動作に問題はないみたいですが・・・*2。とにかく、undefinedでなくかつnullでない要素のみの配列にして返却するメソッドです。

【例】
var ary = [undefined,1,null,3].compact();
alert(ary);
//aryは配列。"1,3"と表示される

flattenメソッド

【抜粋】
  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value.constructor == Array ?
        value.flatten() : [value]);
    });
  },

2次元以上の配列を1次元に変換するメソッドです。injectメソッドに第二引数で渡している関数では、要素の値が配列であるかどうかを、Objectクラスの既存constructorプロパティにより判定しています。配列であれば再帰的にflattenメソッドを呼び出します。こうしてarrayに既存concatメソッドで次々に連結していきます。[value]は、concatするための単要素配列を作成するものです。

【例】
var ary = [0, [11, 12], [[211, 212], [221, 223]]].flatten();
alert(ary);
//aryは配列。"0,11,12,211,212,221,223"と表示される

withoutメソッド

【抜粋】
  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

まず、引数すべてを配列valuesに変換します。自配列thisのselectメソッドと、valuesのincludeメソッド*3により、valuesに存在しない自配列thisの要素の値のみの配列を返却しています。

【例】
var ary = [0, 1, 2 ,3].without(1, 3);
alert(ary);
//aryは配列。"0,2"と表示される

indexOfメソッド

【抜粋】
  indexOf: function(object) {
    for (var i = 0; i < this.length; i++)
      if (this[i] == object) return i;
    return -1;
  },

自配列thisに引数で指定されたオブジェクトが存在する場合にそのインデックスを返却します。なければ-1を返却します。

【例】
var obj0 = new Object();
var obj1 = new Object();
var obj2 = new Object();
var obj3 = obj2;
var obj4 = new Object();
var ary = [obj0, obj1, obj2];
alert(ary.indexOf(obj2)); //"2"と表示される
alert(ary.indexOf(obj3)); //"2"と表示される
alert(ary.indexOf(obj4)); //"-1"と表示される

reverseメソッド

【抜粋】
  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

既存reverseメソッドをオーバーライドしています。引数inlineがfalseでない場合(null,undefiend含む)、自配列thisを_reverseメソッド(既存メソッドを保持)により逆順に並び替えます。falseの場合、toArrayメソッド*4により別配列を作成、これを逆順に並び替えて返却します。

既存メソッドは元の配列も並び替えてしまうので、オーバーライドしてその回避手段を与えているわけです。

!==は「オブジェクトも同一であるかどうかを判定。同一でなければtrue」という意味ですが、ここではどういう場合に有効なのかが分かりません・・・。→「0!=false」はfalseになります。厳密に判定するために!==を使用しているようです。

【参考】
alert(0==false);//true
alert(0===false);//false
var org = [0,1,2,3];
var ary = org.reverse(false);
alert(ary); //"3,2,1,0"と表示される
alert(org); //"0,1,2,3"と表示される
ary = org.reverse();
alert(ary); //"3,2,1,0"と表示される
alert(org); //"3,2,1,0"と表示される

shiftメソッド

  shift: function() {
    var result = this[0];
    for (var i = 0; i < this.length - 1; i++)
      this[i] = this[i + 1];
    this.length--;
    return result;
  },

まず自配列thisの最初の要素の値をresultに保持します。次の要素以降の値をインデックスを-1ずつずらして格納します。その後lengthを-1し、resultを返却します。

shiftメソッドは既存で、ここではオーバーライドしていることになります。が、既存との機能の違いはよく分かりません。下のコードで、要素がない場合に呼び出した場合の挙動に違いがあるようなのですが・・・。→2006/08/01修正。既存では、要素がない場合と、要素の値がundefinedの場合で同じ動作(undefinedを返却)となります。これを避けるためにオーバーライドされていると思われます。→2006/09/01修正。バージョン1.5.0_rc0ではメソッド自体がなくなっています。考えすぎだったみたいです。。。

【例】
var ary = [0,1,2,3];
try{
  alert(ary.shift()); //"0"が表示される
  alert(ary.shift()); //"1"が表示される
  alert(ary.shift()); //"2"が表示される
  alert(ary.shift()); //"3"が表示される
  alert(ary.shift()); //エラーとなる(既存だとundefined)
}catch(e){
  alert(e);
}

inspectメソッド

【抜粋】
  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }

mapメソッド*5によって自配列thisの各要素を前述Object.inspectにより文字列化し、joinメソッドにより「,」で連結しています。Object.inspectメソッドにより呼び出されることが前提です。

*1:後にreverseメソッドを上書きするためです。

*2:AとBに共有部分がないとき、!A||!Bは常にtrue

*3:両方ともEnumerableクラスから継承

*4:Enumerableクラスから継承

*5:Enumerableクラスから継承