やりたいこと
JavaScriptで setTimeout() や setInterval() のタイマハンドラに引数を渡したい。
ダメな書き方
これはダメです。実行するとエラーが発生します。setTimeout() の第1引数に関数ではなく関数の戻り値を渡すことになるからです。しかも、hello()には戻り値がないので、undefined が渡されてしまいます。
function hello(text) { console.log(text); }; const timer = setTimeout(hello('Hello, world!'), 1000);
正しい書き方(1):第3引数以降にハンドラの引数を渡す
こう書けば簡潔ですし、正しく動作しますが、やや分かりにくいです。
function hello(text) { console.log(text); }; const timer = setTimeout(hello, 1000, 'Hello, world!');
正しい書き方(2):第1引数にハンドラを呼ぶ無名関数を渡す
少しだけ長くなりますが、こう書いた方が分かりやすいです。
function hello(text) { console.log(text); }; const timer = setTimeout( function(){ hello('Hello, world!') }, 1000);
正しい書き方(3):bind(引数束縛)を使う
いちおう、こういう書き方もあります。JavaScriptの関数オブジェクトは引数束縛のためのメソッド bind() を持ちます。bind() の第1引数は this を束縛し、第2引数以降が元の関数の引数を束縛します。この例では this が不要なので undefined を渡しています。ですが、すこし分かりにくいですね。
function hello(text) { console.log(text); }; const timer = setTimeout(hello.bind(undefined, 'Hello, world!'), 1000);
【追記】いや、(1) と (2) は違うぞ!
よく考えたら、(1)、(3) と (2) は挙動が違います。(1) と (3) は setTimeout() の実行時に引数の値が確定しますが、(2) はハンドラである無名関数が実行時に hello() に渡す引数の値が評価されます。したがって、引数が変数の場合は下記のように結果が変わることがあります。
function hello(text) { console.log(text); }; let text = 'Hello, world!'; // (1) => Hello, world! // const timer = setTimeout(hello, 1000, text); // (2) => Goodbye, world! const timer = setTimeout(function() { hello(text) }, 1000); // (3) => Hello, world! // const timer = setTimeout(hello.bind(undefined, text), 1000); text = 'Goodbye, world!';