uhyohyo.net

JavaScript初級者から中級者になろう

十一章第九回 strictモード

このページの最終更新日:

今回はstrictモードについて解説します。これはES5で導入された、従来より厳しいモードです。これは日本語に訳しただけですが、厳しいというのはどういうことでしょうか。

代表的な例は、前回も少し話に出た、プロパティのwritable属性です。writable属性がfalseとなっているプロパティは、代入しても変更できません。実際に代入しようとした場合、strictモードでない場合は無視されて何も起きませんが、strictモード中にこれをやるとエラーとなります。このように、strictモードでは間違いや望ましくない挙動に対して、エラーを始めとするより厳しい挙動を示します。

詳しくは後で解説しますが、結論から述べると、JavaScriptプログラムを書く場合は全てstrictモードで書くことが推奨されます

strictモードの利用法

プログラムをstrictモードにする方法は、プログラムの最初に"use strict";(または'use strict';)という式を置きます。これは、プログラムの一番最初でなければなりません。例えばHTML中にscript要素でJavaScriptを書く場合にはこのように真っ先にこの式を書きます。


……
<script>
  "use strict";
  // ……
</script>

また、プログラム全体をstrictモードにするのではなく、ある関数の中だけをstrictモードにすることも可能です。その場合は関数の中身の最初に上の式を書きます。


function foo(){
  "use strict";
  // ……
}

strictモードの影響

プログラムがstrictモードになっているときは、一部のプログラムの挙動が従来と異なります。先に述べたように従来よりもより厳しい挙動になっています。それらのうち影響がありそうなものを見ていきます。

書き込み不可能なプロパティに対する代入

上で述べたように、書き込み不可能なプロパティに対する代入は、strictモードではエラーになります。


var obj = {
  foo: 3,
};

Object.freeze(obj);

obj.foo = 5; // 無視される

(function(){
  'use strict';

  obj.foo = 10; // エラー
})();

グローバル変数の作成

はるか昔に、varを使わずに作った変数はグローバル変数になると解説したような気がします。一方のstrictモードでは、varを使わずに変数を作ることはできません


function makeFoo(){
  foo = 3;
}

function makeBarStrict(){
  'use strict';
  bar = 5;
}

makeFoo();
console.log(foo); // 3

makeBarStrict(); // ここでエラーになる
console.log(bar);

2行目でいきなり変数fooに3を代入していますが、この変数はいままで存在しなかった変数で、しかもvarで宣言されていないのでグローバル変数となります。一方、7行目では同じように変数barへの代入を行っていますがこちらはstrictモードの中です。よって、存在しない変数barへの代入は許可されず、エラーとなります。

とはいえ、makeBarStrictの中でvar bar = 5;とするとbarはローカル変数になってしまいます。strictモードでグローバル変数への代入を行いたい場合、先にvarでグローバル変数を作っておく必要があります。次のようにすると、strictモードの関数内からグローバル変数に代入することができます。


var bar;

function makeBarStrict(){
  'use strict';
  bar = 5;
}

makeBarStrict();
console.log(bar); // 5

strictモードでの挙動は、ミスにより望まないグローバル変数を作ることを防ぐ効果があります。例えば上の例で変数名をタイプミスしてbat = 5;とした場合エラーになり、タイプミスに気づくことができます。strictモードでない場合はエラーが出ないがbarには何も入っていないという事態になり、バグが見つかりにくくなるかもしれません。

関数の引数名は重複できない

strictモードでは、関数の引数の名前が同じ場合は文法エラーになります。よって、これは文法エラーです。


function foo(arg, arg){
  'use strict';
}

逆に言えば、こんなコードもstrictモード以前では許されていたということです。ちなみに、この場合変数argは第2引数に渡された値が入っています。第1引数の値は変数に入りませんが、argumentsを使えば取得することができます。

オブジェクトリテラルのプロパティ名も重複できない

次のように、オブジェクトリテラル中で同じプロパティを複数回定義しているようなものはstrictモードでは文法エラーです。


'use strict';
var obj = {
  foo: 3,
  foo: 'bar',
};

このあたりも、プログラマのミスをなるべく早期に発見できる効果が期待できますね。

argumentsの闇が消える

上で何気なく再登場したargumentsですが、これは実はかなり曲者なオブジェクトで、その特殊な挙動はまさにと呼ぶにふさわしいものです。strictモードでは、argumentsのそのような特殊な挙動がなくなります。argumentsの闇に依存してしまうといけないのでこの講座ではそういったものを紹介していませんでしたが、もし興味がある人は調べてみるとよいでしょう(しかし利用してはいけません)。

関連して、arguments = ['foo', 'bar'];のようなargumentsへの再代入はstrictモードではエラーとなります。

withが使えない

strictモード内ではwith文という文を使うのは禁止されています。この講座ではwith文は紹介していませんので、気になる方は調べてみましょう。

with文を禁止することで、JavaScriptをより最適化することができるそうです。つまり、strictモードを使うことはJavaScriptコードの実行速度の向上に寄与するかもしれないということです。

strictモードの意味

他にも細かい違いがありますが、strictモードにおけるJavaScriptの挙動の違いはだいたいこんな感じです。こうしてみると分かるように、strictモードは一つにはミスの早期発見に貢献してくれます。また、最後にさっくりと紹介しましたがstrictモードにより問題のある挙動が廃止されています。

これらの変更によりJavaScriptがより優れた言語になると考えられますね。しかし問題は、これが変更であるということです。というのも、もし非strictモードの挙動に依存しているプログラムがあったとして、ある日突然JavaScriptの仕様が変わって今まで動いていたプログラムがエラーになったら、JavaScriptの利用者はとても困ります。ですから、こういった変更を行いたくてもそう簡単に行うことはできないのです。

今まで動いていたプログラムがこれからも動くことを後方互換性といいます。つまり、問題のある挙動を廃止したくても、いきなりJavaScriptの仕様を変えてしまうと後方互換性が崩れてしまうため、それはできないのです。

その解決策として導入されたのがstrictモードなのです。つまり、このような変更をstrictモードと明示されたプログラムの中だけに適用することで、従来のプログラム(もちろんstrictモードの宣言はされていないはずです)が壊れてしまうことを防ぎつつ、これから書かれるプログラムには挙動の変更が適用できるようにしたのです。

strictモードの宣言が"use strict";という形になっているのもやはり後方互換性のためです。といっても、今度はブラウザの後方互換性です。これが一番不可解な点は、"文字列";という形になっている点です。わざわざ文字列リテラルにせず、use strict;のような新しい構文を素直に導入すればいいように思えるかもしれません。

そうなっていないのは、古いブラウザに対応するためです。strictモードが最新鋭だった当時は、当然それを使えるのは最新のブラウザのみです。となると、strictモードが認識できない古いブラウザでの挙動が問題となります。use strict;のような新しい構文を導入した場合、古いブラウザでは文法エラーになってしまいます。ということは、古いブラウザではJavaScriptが実行できません。これは大問題です。一方、"use strict";という構文ならば、古いブラウザにとっては何の意味もない文字列が書いてあるだけと認識されます。strictモードにはなりませんが一応実行することはできます。

最後に、strictモードの宣言がプログラムの最初と関数の最初の2箇所で使える理由も同じような感じです。ある巨大なプログラムがあり、せっかくなのでstrictモードに書き換えたいという場合を考えます。しかし巨大なプログラムはどこかで非strictモードの挙動に依存しているためプログラム全体をstrictモードにすると正しく動作してくれません。そのようなとき、関数ごとのstrictモード宣言を使うことで、というより小さな単位ごとにstrictモード化を進めることができるのです。

以上でstrictモードの説明は終わりです。基本的にはこれから新しく書くJavaScriptプログラムでstrictモードを利用しない理由はありませんから、積極的に付けていきましょう。