JavaScriptでopenという名前の関数を作るとどうなるか

背景

今受けている大学の授業でJavaScriptを扱っている。

その授業で window.openという関数を用いて新しいウィンドウを開いたり閉じたりする関数を作るという課題が出ているのだが、この課題に取り組んでいる友人がプログラムが動かないと言っていたので、コードを見たところ下のようなコードを書いていた。

<script>
function open(a) {
    window.open(a);
}
<script>
<body>
    <input type="button" onclick="open('~~~~')">
</body>

このコードが動かない理由について下で書いていく。

何故動かないのか

JavaScriptは関数の外に作成した変数や関数、例えばscriptタグの中に直接書き込んだ変数や関数はグローバル変数やグローバル関数となる。

下のコードは変数messageと関数helloをグローバルな変数や関数として宣言した例だが、window.messagewindow.hello()とやってもmessage変数の参照やhello関数の実行が出来る。

<script>
var message = "Hello, world!";

function hello() {
    alert("hello");
}

alert(window.message); // "Hello, world!"とアラートが出る
window.hello(); // hello関数が実行され、helloとアラートが出る
<script>

何故これで実行出来るのかというと、実はJavaScriptではグローバル変数やグローバル関数を作るということは、グローバルオブジェクトのプロパティとして定義するということを意味する。グローバルオブジェクトとはその実行環境の一番トップにあるオブジェクトのことだが、ここではwindowオブジェクトのことだと思えばいい。

なので関数の外でvar message;function hello() {}windowオブジェクトのプロパティやメソッドを宣言していることと同じなのだ。その為、window.messagewindow.hello()で変数の参照や関数の実行が出来る。

このことを考えると先程のコードが動かない理由が説明できて

<script>
// window.openの再定義
function open(a) {
    // 再定義された window.openを呼び出す
    window.open(a);
}
<script>
<body>
    // クリックしたが最後無限ループ地獄へ
    <input type="button" onclick="open('~~~~')">
    // エラーが出る
</body>

function open(a) ~の記述で、既に存在しているwindow.open関数を再定義する形になる。その再定義したopen関数でwindow.openを呼んでいるのでいつまでも再帰呼び出しがされ、無限ループが発生する。

ちなみにChromeではUncaught RangeError: Maximum call stack size exceeded(…)というエラーが発生する。これはスタックオーバーフローのエラーで、JavaScript再帰処理を行ったときにを行いすぎたときに見かけるエラーである。 FirefoxInternalError: too much recursionとエラーが出た(まんまだな……)。

まとめ

JavaScriptを理解してないとこういうこともある。割と初級者がハマることで「JavaScript グローバル汚染」とかでググると似たようなことが書いてある記事が一杯ある。

一応授業的にはこれを回避する予防線を張ってるみたいで、

ウィンドウを生成する関数createNewWindow ("URL")を作ること.また,引数として表のURLの何番目(n)かを受け取り,対応するウィンドウを閉じる関数closeWindow (n)を作ること.

と書いてあった。でも説明の仕方を考えればグローバルオブジェクトのことを説明できたと思うしフォローのやり方はもっとあるんじゃないかなあと思う。*1

参考

developer.mozilla.org

JavaScriptについて本格的に勉強したいと思ったらここだけを読むといいと思う。他のサイトが全く信用できないわけないないけどMDNは割と信用できる。

パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)

JavaScriptの本はサイ本とこの本しかよく知らない。だいぶ古い本になっちゃったけど言語仕様をわかりやすく書いてあるのでオススメではある。

*1:授業自体は様々な言語に触れて、色々なパラダイムを学ぼうというもの。JavaScriptはイベント駆動型プログラミングのテーマで扱われている。全4回しかなく、限られた時間で説明するために詳しくやらないのはわかる。わかるけど………