👹

【JavaScript】関数名をclose()にするのは気をつけろ

2024/11/08に公開
2

単なる自分のやらかしの共有です。この記事は事実をもとにしたフィクションです。

消し忘れたclose()関数

あるときモーダルコンポーネントを閉じる、close()関数というのを作りました。後日、リファクタリングをするときこの呼び出されているclose()関数がとあるコンポーネントから取り除かれることになりました。しかし誤って1つ削除し忘れてしまい、close()関数が取り残されてしまいます。

このとき、import文なども取り除かれたのですが、何故か未定義エラーなどは出ずに見過ごされてしまいました。(察しの良い人はピンとくるかも)

何故か消えるウィンドウ

そんな中、消し忘れたコンポーネントを含む画面で特定の手順を踏むと開いたブラウザウィンドウが閉じてしまう現象が確認されるようになりました。

どうやら発生源は先程のリファクタリング作業らしい…

window.closeというJavaScriptの組込み関数

この不具合の原因はJavaScriptのwindowオブジェクトに生えているclose()組込み関数です。windowオブジェクトは省略可能なため、close()単体でもwindow.close()関数が実行されます。

そのため先程の消し忘れた関数はそのまま組込み関数のclose()関数として解釈され、エラーにもならずブラウザウィンドウを閉じる[1]関数へと転生してしまったのです。同様にopen()

予防法

原因が分かれば不思議なことはないのですが、このような事故を防ぐ方法もあります。

1 単語の命名をやめる

ローテクな方法ですがcloseopenのような1単語の命名をやめることです。事故予防だけでなく、コード品質の観点からも良い点があります。例えばcloseという関数名は何を閉じるのか明確ではありません。今回で言えばcloseModalという関数名ならバッティングも起こらず、関数の役割として明確でした。関数名は具体的動作を示す方がよいでしょう。

命名規則に関しては以下も参考になります

https://ics.media/entry/220915/

https://ics.media/entry/220929/

2 ESLintで縛る

こちらは機械的な手法で、ESLintのno-restricted-globalsというルールを使って縛るのも手です。

https://eslint.org/docs/latest/rules/no-restricted-globals

このルールは指定したグローバル変数の利用を制限します。今回の件なら

"no-restricted-globals": ["error", "close"]

と指定すればグローバル変数としてのclose()関数の利用を制限でき、反する場合はLintエラーとして検出されます。ほかにもopenlocationといった変数も縛れます。

まとめ

命名は些細なようで大事です。命名によっては今回の事故のようになることもあり、全体のコード品質にも影響を与える可能性があります。1つ1つ丁寧な命名を心がけるとよりよい開発となっていくでしょう。

脚注
  1. window.close()関数はwindow.open()などによるスクリプトによって開かれたウィンドウのみ閉じます。そのためこの現象の発生には特定の手順を踏む必要があります。 ↩︎

Discussion

YAMAMOTO YujiYAMAMOTO Yuji

しかし誤って1つ削除し忘れてしまい、close()関数が取り残されてしまいます。

close関数そのものが取り残されたんじゃなくて、close関数を呼び出している箇所が取り残されてしまった、ということでしょうか?

にしはらにしはら

分かりにくくてすみません。
ご指摘のとおり、「呼び出されている関数」が取り残されてしまいました。

本文も呼び出されている関数が取り残されたことを明確に修正しました。