事の発端

同僚プログラマが「ヘイ、こんなコードを書いたけど意図した動作をしないんだ!」と言って次のようなコードを持ってきました。

window.addEventListener("unload", alert("よろしいですか?"))

確かに、これを実行すると unload がトリガーになっているにも関わらずロード時にアラートが表示されてしまいます。
この場合は次のように直せば意図したように動作します。

window.addEventListener("unload", function() { alert("よろしいですか?") })

しかし、何故後者は正しく動くのでしょうか?

最初に結論を書いてしまうと前者はメソッドの実行結果(ちなみに常に undefined を返します)が addEventListener の第二引数として渡されてしまうためです。

これだけ読んでもよく分からないぞ、という方のために順番に説明していきます。

詳しい解説

alert とは何なのか?

正確には window.alertwindow オブジェクトのメソッドです。実行すると皆さんご存知の通りアラートウインドウが表示されて undefined を返します。

メソッドとは関数ではないのか?

メソッドはオブジェクトが持つプロパティの内、関数オブジェクトを参照しているものの総称です。

var func = function () { alert("test") }
var obj = {}
// メソッドの登録
obj.myFunc = func

関数オブジェクトとは?

JavaScript では関数もオブジェクトとして扱います。

var i = 1
var func = function () { alert("test") }

これはどちらも正しく動作します。

console.log(i)
console.log(func)

一方でコンソールから中身を確認すると、ブラウザにより多少の差はあると思いますが後者では関数のオブジェクトが表示されると思います。

このように JavaScript では関数オブジェクトそのものも変数として参照させる事が可能です。

関数の実行について

ここで先ほどの例を少し書き換えてみます。

var func = function () { alert("test") }
var obj = {}
// 関数名の後ろに () がついているのに注目
obj.myFunc = func()

このコードを動作させると obj.myFunc = func() の行が実行された瞬間にアラートが表示されると思います。なぜなら () をつけると関数が実行された結果を右辺に代入しようとするためです。

var func = function () {
  return alert("test")
}

が実行される事になるので alert() が呼び出されて obj.myFunc には undefined が登録されます。関数オブジェクトそのものを参照させたい時は () をつけずに使います。

無名関数について

今までの例では func という名前の変数を用意していましたが、無名関数を用いる事もできます。

単なる数値の計算ではいちいち変数を作らず行う事がよくあると思います。

// 変数にしなくても整数が表示できる
console.log(1 + 2)

繰り返し述べているように関数も変数として扱えるので、整数を書く感覚でメソッド登録時に関数オブジェクトを直接記述する事ができます。

var obj = {}
obj.myFunc = function() { alert("test") }

振り出しに戻る

では、改めて最初の動作したコードを見てみましょう。

window.addEventListener("unload", function() { alert("よろしいですか?") })

ここまで書いて来た内容をまとめると

var func = function() { alert("よろしいですか?") }
window.addEventListener("unload", func)

を無名関数で書いたんだな、という事

window.addEventListener("unload", alert("よろしいですか?"))

と書いてしまうと alert("よろしいですか?") の実行結果が第二引数になってしまう事が、それぞれお分かり頂けたでしょうか。

addEventListener の実装を確認する

最後に、念のため addEventListener の実装を確認してみます。

https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener

第二引数 listener については「指定されたタイプのイベントが発生するときに通知を受け取るオブジェクト」と書かれており、サンプルの中でも modifyText() として定義した関数オブジェクトそのものを第二引数に引き渡しています。