Prototype & script.aculo.us 4章まとめ 1

Prototype & script.aculo.us を買った。なかなか面白いなー。
4章の「Enumerable で高度なコレクション」の自分用メモとして書いておく。
残りの続きはまた書こう。


Prototype & script.aculo.us ―JavaScriptライブラリによるAjaxアプリケーション開発

Prototype & script.aculo.us ―JavaScriptライブラリによるAjaxアプリケーション開発


each による反復処理(イテレーション)

Enumerable の心臓部として、反復メソッドの中心となる each() メソッドがある。

each(iterator [, context] ) -> enumerable

////prototype.js 1.6.0.2 589行目
var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },


$break 列挙を途中で切り上げる

for や while を使った定番のループ処理では、continue と break でループを切り上げることができた。
では、each() を使った時はどうするか?

$break という名前の特別な例外定数を使う。

var result = [];
$R(1, 10).each(function(n) {
	if (0 == n % 2) return;
	if (n > 6) throw $break;
	result.push(n);
});

// result -> [1,3,5]


$continue は?


もともと反復処理を中断させるために、$break と $continue が用意されていた。
が、後者は return で代用ができるのと、パフォーマンスが優れているため、
Prototype 1.5.1 から追放された。


列挙のコンテキスト


Prototype 1.6 から、すべての Enumerable メソッドに context という引数が新たに追加された。
これは、必要に応じてJavaScript 1.6 以降の動作を真似る目的で導入された、とのこと。

var items = [1,2,3];
items.all(function(item) { console.log(this)});
// Window
// this は Window オブジェクトを指し示す

var items = [1,2,3];
items.all(function(item) { console.log(this)}.bind(items));
// 1,2,3
// bind してオブジェクトを渡せるが、

var items = [1,2,3];
items.all(function(item) { console.log(this)}, items);
// 1,2,3
// こうも渡せるようになった。


使用するメソッドが JavaScript1.6 以降で提供されている場合(メソッドがネイティブな場合)
この引数を使用すると、手作業でイテレータにバインドするのと比べて格段に早くなる。
(ループのたびに、bind() の作成する無名関数によるラッピングのコストを節約できるため)


メソッドの集合


all/every ([ iterator = Prototype.K ]) -> Boolean
any/some ([ iterator = Prototype.K ]) -> Boolean
include/menber(value) -> Boolean
size() -> Number


all/every, any/some, include/menber は別名(エイリアス)。
Prototype.K は特別な関数。働きとしては、最初の引数を受け取り、そのまま何もせずに返す。
all() メソッドも any() メソッドも、反復処理を中断すべき条件に遭遇したら、反復を中止する。
include/member は == ではなく、=== を使用している。


size() メソッドはコレクションの要素数を返す。

var animals = ['cat', 'dog', 'bird'];
animals.size();
// 3


要素を見つけることと、フィルタを適用すること



detect/find(iterator) -> firstElement | undefined
filter/findAll/select(iterator) -> Array
grep(pattern [, iterator = Prototype.K] ) -> Array


find/detect() メソッドはイテレータによって、記述される条件にマッチする最初の要素を返す。
条件がマッチする要素がない場合は何も返さないので、結局 undefined を返すことと同じになる。

var str = ['a', 'aa', 'aaa', 'aa', 'a'];
console.log(
 str.find(function(s) {
  return s.length >= 3;
 })
);
// aaa
console.log(
  str.find(function(s) {
   return s.length >= 4;
  })
);
// undefined

//prototype.js 1.6.0.2 640行目

detect: function(iterator, context) {
  iterator = iterator.bind(context);
  var result;
  this.each(function(value, index) {
    if (iterator(value, index)) {
      result = value;
      throw $break;
    }
  });
  return result;
},

filter/findAll/select() メソッドはマッチするすべての要素の配列を返す。

var str = ['a', 'aa', 'aaa', 'aa', 'a'];
str.select(function(s) {
  return s.length >= 2;
})
//aa,aaa,aa


grep() メソッドは findAll の特殊なバリエーション。正規表現も使える。
findAll() と同じように、配列要素のうち条件にマッチするものをすべて返す。
また要素そのものではなく、要素を処理した結果の値を使う必要がある場合は、
イテレータを渡すこともできる。

$R(1,30).grep(/[05]$/);
//5,10,15,20,25,30
$R(1,30).grep(/[05]$/, function(n) { return n -1; });
//4,9,14,19,24,29