prototype.jsソースコード分析

改めてEnumerableのeachメソッド

  • iterator.bind」のiteratorにはbindメソッドが実装されているFunctionクラスの引数がこなければならい。contextにはbindしたいオブジェクトということでしょうか
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;
  }
}

bindメソッドで使用している$A関数のデバッグとargumentsオブジェクト

<script>
function test(){
	w(typeof arguments);
	w(arguments.constructor);
	w(arguments.length);
	w(arguments[0].constructor);
//	w(arguments[1].constructor);
	w(arguments[0].dasi);
//	w(arguments[1].dasi);
	w('以下bindメソッドで使用している$A関数のデバッグ');
	var args = $A(arguments);
	w(typeof args);
	w(args.constructor);
	w(args);
	w(args.shift());
	w(args.concat($A(arguments)));
	//args.shift()で配列からオブジェクトを抜いてしまったので
	//ここでもう一度走査して取得しなおしている。のかな。

}

function $A(iterable){
	var length = iterable.length, results = new Array(length);
	while(length--) results[length] = iterable[length];
	return results;
}

function loopChk(data){
	for(p in data){
		w(p);
	}
}
function w(data){
	document.write(data + "<hr>");
}

//----------------------------------
var Nabe = function(dasi){
	this.dasi = dasi;
	this.say = function(){
		alert(this.dasi);
	}
};

var fuguchiri = new Nabe('konbu');
var sukiyaki = new Nabe('mirin');
test(fuguchiri);
//test(fuguchiri, sukiyaki);

</script>

最小スペックのbindメソッド

  • 以下のように書くとbindメソッドが何をやってくれているのかスッキリすると思います。
<script>
var Nabe = function(dasi){
	this.dasi = dasi;
	this.say = function(){
		alert(this.dasi);
	}
}
Function.prototype.bind = function(){
	return function(){
		alert('引き渡したオブジェクトのメソッドを実行してもらいたい');
	}
}

//*--------------------------------*//
var fuguchiri = new Nabe('konbu');
//fuguchiri.say();
setTimeout(fuguchiri.say, 500);
setTimeout(fuguchiri.say.bind(fuguchiri), 500);
</script>
  • さらに実際のbindに近づけると以下のような感じか。
  • argumentsって何気につかってるけど、ちょっと整理する必要がありそうだ。
  • thisのスコープというかなんと言うか曖昧だった部分が少しわかったような気がしてきた。
<script>
var Nabe = function(dasi){
	this.dasi = dasi;
	this.say = function(){
		alert(this.dasi);
	}
}
Function.prototype.bind = function(){
	if(arguments.length < 2 && arguments[0] === undefined) return this;
	var __method = this;//よく参考書で見る var self = thisのとこかな
	var object = $A(arguments).shift();
	//for(p in object){
	//	alert(p);
	//}
	return function(){
		return __method.apply(object, arguments);
	}
}
function $A(iterable){
	if(!iterable) return [];
	if(iterable.toArray) return iterable.toArray();
	var length = iterable.length, results = new Array(length);
	while(length--)
		results[length] = iterable[length];
	return results;
}

//*--------------------------------*//
var fuguchiri = new Nabe('konbu');
//fuguchiri.say();
//setTimeout(fuguchiri.say, 500);
setTimeout(fuguchiri.say.bind(fuguchiri), 500);
</script>

再びbindメソッド

    • 一度載せましたがどうも勘違いしていた箇所がありそうなので引っ込めました><
    • 再掲です。何を「bind」するのか
<script>
Object.extend = function(destination, source){
	for(p in source){
		destination[p] = source[p]
	}
	return destination;
}
Object.extend(Function.prototype, {
	bind : function(){
		if(arguments.length < 2 && arguments[0] === undefined) return this;
		var __method = this, args = $A(arguments), object = args.shift();
		return function(){
			return __method.apply(object, args.concat($A(arguments)));
		}
	},
	test :  function(){
		var __method = this, args = $A(arguments), object = args.shift();
		return function(){
			alert(__method);//これをbindしたい。
			alert(this);    //こうしたくない
		}
	}
});
function $A(iterable){
	if(!iterable) return [];
	if(iterable.toArray) return iterable.toArray();
	var length = iterable.length, results = new Array(length);
	while(length--){
		results[length] = iterable[length];
	}
	return results;
}
</script>
<script>
var Nabe = function(dasi){
	this.dasi = dasi;
	this.say = function(){
		alert(this.dasi);
	}
};

var fuguchiri = new Nabe('konbu');
//setTimeout(fuguchiri.say, 500);
setTimeout(fuguchiri.say.test(fuguchiri), 500);
setTimeout(fuguchiri.say.bind(fuguchiri), 500);
</script>

$A関数

  • $A関数の引数のコンストラクタにtoArrayメソッドが実装されていればそのメソッドを返す。
  • ざっと確認したところではtoArrayメソッドはStringクラスとArrayクラスに実装されている
    • 432行目→直接Stringクラスをextend
    • 771行目→EnumerableオブジェクトのメソッドとしてArrayクラスをextend。だが、932行目の「Array.prototype.toArray = Array.prototype.clone」でArrayクラスのtoArrayメソッドは以下の通り
clone : function () {
    return [].concat(this);
}
  • document.getElementsByTagNameで取得した値のコンストラクタはObjectクラスなのでtoArrayメソッドはスルー。
  • 配列で試してみるとtoArrayメソッドが実装されているのでそちらを返す。判別のために若干いじっています。
  • 以下をコピペで動作します。
<script>
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}
Array.prototype.clone = function(){
	return "hoge";//[].concat(this);
}
Array.prototype.toArray = Array.prototype.clone;

</script>
<script>
window.onload = function(){
	//var ele = document.getElementsByTagName('DIV');
	var ele = [1,2,3];
	alert(ele.constructor);
	alert(Array.prototype.toArray);
	alert($A(ele));
}
</script>
<div>hoge</div>
<div>hoge</div>
<div>hoge</div>