読者です 読者をやめる 読者になる 読者になる

Underscore.jsおさらい4(Functions)

bind _.bind(function, object, *arguments)

関数のオブジェクト束縛。関数を変数にして引き回せるJavaScriptならでは。
Javaな自分には最初理解できなんだ。
関数内のthisをオブジェクトに割り当てる。
今は標準でbindが実装されているが、実装が曖昧という記述も見たのでしばらくは使うかも。

var func = function(greeting){ return greeting + ': ' + this.name };
func = _.bind(func, {name: 'moe'}, 'hi');
func();
=> 'hi: moe'


bindAll _.bindAll(object, *methodNames)

methodNamesで指定したメソッドだけbindする。未指定なら全メソッドが対象。

var buttonView = {
  label  : 'underscore',
  onClick: function(){ return 'click:' + this.label; },
  onHover: function(){ return 'hovering:' + this.label; }
};
_.bindAll(buttonView, 'onHover');
var click = buttonView.onClick;
var hover = buttonView.onHover;
click();    => click:undefined
hover();    => hovering:underscore


partial _.partial(function, *arguments)

bindはthisをbindするが、partialは引数をbindする感じ。
第一引数はbindせずに、第二引数をbindしたい、という場合は「_」を使えばいい。

var subtract = function(a, b) { return b - a; };
sub5 = _.partial(subtract, 5);
sub5(20);
=> 15

// Using a placeholder
subFrom20 = _.partial(subtract, _, 20);
subFrom20(5);
=> 15


memoize _.memoize(function, [hashFunction])

関数をキャッシュして処理を早くする用途で使われる。

var fibonacci = _.memoize(function(n) {
  return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
});


delay _.delay(function, wait, *arguments)

setTimeoutに近い感じで使える。waitミリ秒後にfunctionを実行する。

var log = _.bind(console.log, console);
_.delay(log, 1000, 'logged later');
=> 'logged later' // 1秒後にコンソール出力


defer _.defer(function, *arguments)

コールスタックが空になるまで処理を待つ。setTimeoutで0ミリ秒指定と似た感じ。

function() {
  alert('1');
  _.defer(function(){ alert('deferred'); });
  alert('2');
}
=> 1, 2, deferrdの順にalert表示


throttle _.throttle(function, wait, [options])

関数の呼び出しを制限できる。
waitミリ秒以内に関数が再度呼び出されても実行されないようになる。
optionsに{leading: false}を指定すれば、初回実行も指定秒待ってから実行される。
{trailing: false}を指定すれば、指定時間内に後続の呼び出しがあれば、待たずに実行がキャンセルされる。
特にleading: falseの時とか、特に引数とかbindした関数使うときは注意。

var f = _.throttle(function(n) {console.log(n + ':' + _.now());}, 100);
f(1);  => 現在ミリ秒表示
f(2);  => 1回目から100ミリ秒以上経っていれば実行、経っていなければ100ミリ秒後まで待って実行

var f = _.throttle(function() {console.log('-----:' + _.now());}, 100, {leading: false});
f();  => 100ミリ秒後に実行されるようにtimerセット
f();  => 開始から100ミリ秒以内であれば、1回目の呼び出しを破棄して開始から100ミリ秒後に実行されるようにtimerセット
// 後勝ちになる

var f = _.throttle(function() {console.log('-----:' + _.now());}, 100, {trailing: false});
f();  => 現在ミリ秒表示
f();  => 1回目から100ミリ秒以内であれば無視される、100ミリ秒以上なら実行


debounce _.debounce(function, wait, [immediate])

関数の呼び出しを制限できる。
throttleの{leading: false}と似たような動きになる。
immediateをtrueにすると、まず関数が実行されるのでthrottleの{trailing: false}と似た感じ。

once _.once(function)

一度しか実行できない関数にする。2回目以降の戻り値は1回目と同じ値になる。

var f = _.once(function() {console.log('-----:' + _.now());});
f();  => 実行
f();  => 実行されない


after _.after(count, function)

count回呼び出し以降しか実行されない。

var f = _.after(3, function() {console.log('-----:' + _.now());});
f();  => 実行されない
f();  => 実行されない
f();  => 実行
f();  => 実行


before _.before(count, function)

最初のcount - 1回までしか実行されない。
onceは_.before(2, function)として実装されている。

var f = _.after(3, function() {console.log('-----:' + _.now());});
f();  => 実行
f();  => 実行
f();  => 実行されない
f();  => 実行されない


wrap _.wrap(function, wrapper)

関数を別関数でラップして、元関数の前後に処理を入れられる。
AOPとかfilterとかそんなイメージ。

var hello = function(name) { return "hello: " + name; };
hello = _.wrap(hello, function(func) {
  return "before, " + func("moe") + ", after";
});
hello();
=> 'before, hello: moe, after'


negate _.negate(predicate)

predecateの戻り値(真偽値)を逆にする。

var f = _.negate(function(n){ return n % 2 == 0; });
_.find([-2, -1, 0, 1, 2], f);
=> -1


compose _.compose(*functions)

合成関数を作成する。
数学的にf(), g(), h()の合成関数はf(g(h()))となるらしいので、後ろの関数から順に処理される感じになる。

var greet    = function(name){ return "hi: " + name; };
var exclaim  = function(statement){ return statement.toUpperCase() + "!"; };
var welcome = _.compose(greet, exclaim);
welcome('moe');
=> 'hi: MOE!'

Function終了。タイマー系の理解がちょっと大変。