Strict ModeはJavaScriptのより厳格なバリアント
JavaScriptは歴史が長いぶん、今となっては首をかしげたくなる仕様をたくさん抱えています。代入がこっそりグローバル変数を生やしてしまったり、thisが呼び出し方次第でコロコロ意味を変えたり、同じ名前の仮引数を複数書いてもなぜか通ってしまったり……。とはいえ、これらを今さら変更するとWeb全体が壊れてしまうため、言語仕様を管理するTC39は別のアプローチを取りました。オプトインで有効化できるモードを用意して、そこでは尖った部分を削り落とす、というやり方です。
このオプトインが、いわゆる strict mode(厳格モード)です。ファイルや関数の先頭に1行書くだけで有効になります。
これを実行すると ReferenceError: x is not defined が出ます。ディレクティブを書かなければ、同じコードが x というグローバル変数を勝手に作って 10 を出力します。同じ言語なのに、ルールが2種類あるわけです。
'use strict' ディレクティブとは
ディレクティブの正体はただの文字列リテラルで、'use strict'; もしくは "use strict"; と書きます。配置場所はスクリプトまたは関数の先頭の文でなければいけません。これより前に何か、たとえ式がぽつんと置かれているだけでも、エンジンはただの文字列とみなしてスルーします。
ストリクトモードは関数単位でも有効にできます。ディレクティブをその関数の中に書けばOKです。とはいえ、今となってはこの書き方をする人はほぼいません。モジュールを書けば自動的にストリクトモードになりますし、そうでなければファイル全体に効かせるのが普通です。
strict modeで実際に何が変わるのか
ストリクトモードは単一のルールではなく、いくつかの変更がセットになったものです。代表的なものを挙げると:
- 宣言していない変数に代入するとグローバルが作られずエラーになる。
- 通常の関数呼び出し内の
thisがグローバルオブジェクトではなくundefinedになる。 - 引数名が重複していると (
function f(a, a) {}) シンタックスエラー。 - 読み取り専用プロパティへの代入が、黙って失敗せずにエラーを投げる。
- ただの変数や関数を
deleteしようとするとエラー。 - 将来の予約語 (
implements、interface、package、private、protected、public、static、yield) を変数名として使えない。
簡単な例を見てみましょう:
どれも共通しているのは「エンジンが早い段階でバグっぽい挙動に気づいてくれる」ということ。strict モードの価値はまさにそこに尽きます。
こっそり作られるグローバル変数問題
strict モードが存在する一番の理由がこれです。非 strict だと、ちょっとしたタイポがそのままグローバル変数になってしまいます。
// 非strictモード(こうしてはいけない)
function setup() {
usernmae = 'Ada'; // タイプミス — window.usernmae が作成される
}
setup();
console.log(username); // undefined — 本来の変数は設定されていない
プログラムは動いてしまいます。エラーも出ません。バグが表面化するのは、もっとあとになってから。本来 username を読むはずだった箇所が undefined を返して、ようやく「あれ?」となるわけです。strictモードなら、同じタイポでも実行された瞬間にエラーが飛んでくるので、何時間も悩まず数秒で直せます。
最近のJavaScriptはデフォルトでstrictモード
ここで多くの人がつまずくポイントがあります。実は、今書いているJavaScriptのほとんどは、'use strict' なんて一行も書かなくても、すでにstrictモードで動いているのです。
自動的にstrictモードになるケースは主に2つ:
- ESモジュール:
.mjsファイル、<script type="module">、そしてimport/exportで読み込まれるコードは、すべてstrictモードで実行されます。ディレクティブを書く必要はありません。 - クラスの中身:
class { ... }の中に書かれた文は、周りのファイルがstrictでなくても、すべてstrictモードとして扱われます。
では実際のところ、'use strict'; を自分で書く必要があるのはどんなときでしょうか?答えは クラシックスクリプト のときだけです。つまり、素の <script> タグで読み込む昔ながらの JS や、モジュールバンドラを使わずに require で動かしている古めの Node.js ファイルですね。新しく書くコードなら、モジュールが勝手に strict モードにしてくれます。
strict モードでは通常関数の this が変わる
初心者がよくハマるポイントなので、ここはしっかり押さえておきましょう。非 strict モードでは、オブジェクトを前に付けずに普通の関数を呼び出すと、this はグローバルオブジェクト(ブラウザなら window、Node なら global)に紐づきます。一方、strict モードでは undefined になります。
たいていの場合、この挙動こそが望ましいものです。this が undefined になっていれば、メソッドのバインドを忘れたバグはその場で表面化します。これがグローバルオブジェクトをこっそり指してしまうと、ずっと後の工程で何かが壊れるまでバグが隠れ続けることになります。
strict モードが してくれないこと
strict モードはあくまでコンパイル時・実行時のチェックを厳しくする仕組みであって、型を付けてくれるわけでも、null 参照を検出してくれるわけでも、未使用変数を警告してくれるわけでもありません。そうした仕事は TypeScript や ESLint、エディタの担当です。strict モードは、言語レベルで踏みやすい特定の地雷をつぶしてくれるもの、くらいに考えておきましょう。あらゆる安全網の代わりにはなりません。
また、「モダンな JavaScript」と strict モードはイコールではありません。strict モードでもひどいコードは書けますし、ディレクティブなしでも素晴らしいコードは書けます。あくまで、いくつかの特定のミスをしにくくしてくれるだけです。
ここまでのポイント
- ファイルや関数の先頭に
'use strict';を書くと、より厳格な JavaScript のバリアントを使うことになります。 - タイプミスで勝手にグローバル変数が作られたり、
thisがいつの間にかグローバルオブジェクトを指したりといった定番の地雷が、エラーとして検出されるようになります。 - ES モジュールや
classのボディは自動的に strict モードです。自分でディレクティブを書く場面はほとんどありません。 - 新機能を有効化するためのものではなく、バグを早めに捕まえるための仕組みです。
次回:コメント
ここまででディレクティブ、セミコロン、そしてエンジンが気にするルールを見てきました。次のテーマは、あなた自身 が未来の読み手に残すメモ——JavaScript のコメントの書き方と、どんなときに書く価値があるのかを見ていきます。
よくある質問
JavaScriptのstrictモードとは何ですか?
strictモードは、JavaScriptの「厳しめバージョン」を有効にするオプトインの仕組みです。普段なら黙って通ってしまうようなミスを、きちんとエラーとして検出してくれるようになります。使い方は簡単で、ファイルや関数の先頭に 'use strict'; と書くだけ。これを入れると、未宣言の変数への代入がエラーになる、関数の仮引数名の重複が禁止される、関数呼び出し時の this がグローバルオブジェクトではなく undefined になる、といった変更が入ります。
strictモードはどうやって有効にしますか?
スクリプトや関数の一番最初の文として 'use strict'; を書きます。コメント以外の文はもちろん、無関係なコードが1行でも前にあると、ただの文字列リテラルとして無視されてしまうので要注意です。とはいえ現代のコードでは自分で書く機会はほぼありません。ES modules(.mjs や <script type="module">)と、class の内部は自動的にstrictモードとして扱われるからです。
なぜstrictモードを使うべきなのですか?
バグを早い段階で潰せるからです。たとえば usernmae = 'Ada' のようなタイプミスは、非strictモードだと黙ってグローバル変数が作られてしまいますが、strictモードなら ReferenceError で止まってくれます。他にも将来の予約語を先取りで禁止したり、古いエンジンではプロパティ名の重複を禁止したり、this の挙動を予測しやすくしたりと、コードの見通しを良くしてくれる効果がいろいろあります。
今どきのJavaScriptでも 'use strict' を書く必要はありますか?
'use strict' を書く必要はありますか?基本的には不要です。ES modulesも class の中身もデフォルトでstrictモードですし、最近のプロジェクトはほぼどちらかに該当します。明示的に 'use strict'; を書く必要があるのは、type="module" を付けずに <script> で読み込む昔ながらのクラシックスクリプトくらいです。