Prototype.js 1.6 コードリーディング #Function 編
ほとんど「Prototype.js 1.6.0 の変更点」の焼き直しなんですが、自分の言葉で書きとめておくのもいいかと思い、エントリにしました*1。
目次
- Function#argumentNames()
- Function#bind(thisObj, arg0, ...)
- Function#bindAsEventListener(thisObj, arg0, ...)
- Function#curry(arg0, ...)
- Function#delay(seconds, arg0, ...)
- Function#wrap(wapperFunc)
- Function#methodize(func)
- Function#defer(func)
本文
Function#argumentNames()
関数の引数名を配列で返す。
例:
(function(){ alert(new Function("a","b","return a+b").argumentNames()) })() // => a, b
どう実現しているかというと、Function#toString() で得られるソース文字列から正規表現で持ってきている。
まず使い道の無いメソッドで、Prototype.js 内でも継承でしか使っていない*2。
Function#bind(thisObj, arg0, ...)
Function#apply(thisObj, arguments) のラッパー。apply を実行する function を返す。
JavaScript では function を引数に渡すということがよくあるけど、
(function(){ setTimeout(function() { alert(null, '1秒後に alert!') }, 1000); })()
これが
(function(){ setTimeout(alert.bind(null, '1秒後に alert!'), 1000); })()
のように書ける。「function」と書く回数が減らせる。
bind の第一引数は apply の第一引数と同じ意味。上の alert ではあまり関係ないけれど、
(function(){ var Hoge = Class.create({ methodA: function(msg) { this.msg = msg; setTimeout(this.methodB.bind(this), 1000); }, methodB: function() { alert(this.msg); } }); new Hoge().methodA('aaa'); })()
のように書ける。
ちょっとトリッキーだけど、
(function(){ alert([].slice.bind(arguments)().inspect()) // => [1, 2, 3] })(1,2,3)
なんてのもできる。
Function#bindAsEventListener(thisObj, arg0, ...)
bind のイベントリスナ用。これを使うと、返される function の第一引数にイベントオブジェクトを渡してくれる。
例:
(function(){ function listener(e, msg) { alert(msg + ' clientX:' + e.clientX + ' clientY:' + e.clientY); } Event.observe(document.body, 'click', listener.bindAsEventListener(this, 'clicked!'), false); })()
Function#curry(arg0, ...)
カリー化。やっている内容は「JavaScript で引数束縛: Days on the Moon」と同じ。ただ「JavaScript でカリー化、再び」を読んだ後だと少々物足りない。
例:
(function(){ function sum(a,b,c) { return a + b + c } var sum10 = sum.curry(10); alert(sum10(1,2)); // => 13 alert(sum10(1)); // => 10 + 1 + undefined => NaN var sum10_20 = sum.curry(10, 20); alert(sum10_20(1)); // => 31 })()
Function#delay(seconds, arg0, ...)
setTimeout のラッパー。
例:
(function(){ var Sleeper = Class.create({ wakeup: function(msg) { alert(msg + ' ' + this.sec); }, wakeupLater: function(sec) { this.cancelWakeup(); this.sec = sec; this.tid = this.wakeup.bind(this).delay(sec, sec <= 1 ? 'Good morning!' : 'Good evening!'); }, cancelWakeup: function() { if (this.tid) clearTimeout(this.tid); this.tid = null; } }); var sleeper = new Sleeper(); sleeper.wakeupLater(1); sleeper.wakeupLater(3); })()
Function#wrap(wapperFunc)
wrapperFunc の第一引数に自分を渡す function を返す*3。継承のところで $super にスーパークラスのメソッドを渡すのに使われている。
例:
(function(){ function hoge() { alert('hoge') } var wrapped = hoge.wrap(function(w) { alert('before'); w(); alert('after'); }); wrapped(); })()
Function#methodize(func)
関数をメソッドにする。と言うとちょっと変な感じだけど。
func の第一引数に this を渡す function を返す。
(function(){ // http://developer.mozilla.org/ja/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach#Compatibility function forEach(array, fun /*, thisp*/) { var len = array.length; if (typeof fun != "function") throw new TypeError(); var thisp = arguments[2]; for (var i = 0; i < len; i++) { if (i in array) fun.call(thisp, array[i], i, array); } } forEach([1,2,3], function(v) { alert(v) } ); if (!Array.prototype.forEach) { Array.prototype.forEach = forEach.methodize(); } [1,2,3].forEach(function(v) { alert(v) }); })()
Function#defer(func)
おまけメソッド。短いから転載。
Function.prototype.defer = Function.prototype.delay.curry(0.01);
たぶん curry って言いたいだけ。
参考
http://www.prototypejs.org/
Prototype 1.6.0, script.aculo.us 1.8.0, and the Bungee book now available
http://www.prototypejs.org/2007/11/7/prototype-1-6-0-script-aculo-us-1-8-0-and-the-bungee-book-now-available