ECMAScript第5版では、すべての主要なブラウザー(IE10 を含む)で今実装されている strict モードが導入されています。ウェブブラウザーに厳密 (strict) に解釈させるのは簡単ですが (ソースコードの先頭に'use strict';
を追加するだけ)、既存のコードベースを strict モードに移行するには、もう少し作業が必要です。
この記事では、開発者のためのガイダンスを提供することを目的とします。
段階的移行
strict モードは、段階的に移行できるように設計されています。ファイルごとに個別に変更することもできますし、関数の粒度で strict モードにコードを移行することも可能です。
非strict と strict の違い
構文エラー
'use strict';
を追加すると、スクリプトが実行される前に、以下のケースではSyntaxError
をスローします。
- 8進数構文
var n = 023;
with
ステートメント- 変数名での
delete
の使用delete myVariable
; - 変数または関数の引数名としての
eval
やarguments
の使用 - (ECMAScript 2015 以前での) 新しい reserved keywords の使用 (
implements
,interface
,let
,package
,private
,protected
,public
,static
,yield
) - ブロック内での関数宣言
if(a<b){ function f(){} }
- 明らかなエラー
- オブジェクトリテラルでプロパティ名に同じ名前を 2 回宣言
{a: 1, b: 3, a: 7}
これは ECMAScript 2015 ではこのケースはもはや存在しません(バグ 1041128)。 - 同じ名前の 2 つの関数の引数の宣言
function f(a, b, b){}
- オブジェクトリテラルでプロパティ名に同じ名前を 2 回宣言
単純なエラーや悪習を明らかにするので、こういったエラーは良いものです。これらのエラーは、コードが実行される前に発生します。
新しいランタイムエラー
JavaScript はかつて実行されたものがエラーとなるコンテキストに黙って陥っていました。strict モードでは、そのような場合にエラーをスローします。もしコードベースに、このような場合が含まれているなら、何も壊れていないことを確認するために、テストが必要です。繰り返しますが、関数の粒度レベルで発生する可能性があります。
未宣言変数への値設定
function f(x){ 'use strict'; var a = 12; b = a + x * 35; // エラー! } f();
これはめったに期待される効果はないグローバルオブジェクトの値を変更するために使用されます。本当にグローバルオブジェクトに値を設定したい場合は、それを引数として渡し、明示的にプロパティとして割り当てます:
var global = this; // トップレベルのコンテキストでは、"this" は常に // グローバルオブジェクトを参照します function f(x){ 'use strict'; var a = 12; global.b = a + x * 35; } f();
設定不可能なプロパティの削除
'use strict'; delete Object.prototype; // エラー!
非 strict モードでは、ユーザーの期待に反し、黙って失敗します。
ポイズン引数と関数プロパティ
arguments.callee
, arguments.caller
, anyFunction.caller
, または anyFunction.arguments
にアクセスすると strict モードではエラーをスローします。唯一の合法的なユースケースは、以下のように関数を再利用することでしょう。
// example taken from vanillajs: http://vanilla-js.com/ var s = document.getElementById('thing').style; s.opacity = 1; (function(){ if((s.opacity-=.1) < 0) s.display="none"; else setTimeout(arguments.callee, 40); })();
上記は以下のように書き換えられます。
'use strict'; var s = document.getElementById('thing').style; s.opacity = 1; (function fadeOut(){ // name the function if((s.opacity-=.1) < 0) s.display="none"; else setTimeout(fadeOut, 40); // 関数名を使用する })();
意味的な違い
これらの違いは非常に微妙な違いです。テストスイートはこの種の微妙な差を捉えない可能性があります。コードベースを慎重に見直し、おそらくこれらの違いは、コードの意味に影響を与えないことを確認するために必要になります。幸いなことに、この慎重な検討は、関数の粒度で徐々に行えます。
関数呼び出しでの this
f()
のような関数呼び出しで、this
値はグローバルオブジェクトでした。strict モードでは、今undefined
です。関数が call
または apply
で呼び出されるとき、値がプリミティブな値であった場合、これはオブジェクト(または、undefined
や null
に対してはグローバルオブジェクト)にボックス化されました. strict モードでは、値は、変換または交換せずに直接渡されます。
arguments
が名前付きの関数の引数をエイリアスしない
非strict モードでは、arguments
オブジェクト内の値を更新すると、対応する名前付きの引数を更新します。これは JavaScript エンジンのための複雑な最適化を行い、読み/理解しにくいコードを作りました。strict モードでは、arguments
オブジェクトは名前付き引数と同じ値で生成され初期化されています。しかし、arguments
オブジェクトか名前付きの引数への変更はお互いに反映されていません。
eval
への変更
strict モードコードでは、eval
は呼び出される範囲で新しい変数を生成しません。また、strict モードでは、もちろん、文字列は strict モードのルールで評価されます。徹底的なテストは確かに何も破綻しないのを確認するために実行する必要があります。本当に必要ではない場合、eval を使用しないのが、別の実用的な解決策であるかもしれません。
厳密性に中立なコード
strict コードを strict モードに移行するポテンシャルの"downside"は、セマンティクスは、strict モードを実装していないレガシーブラウザーでは異なる場合があることです。(悪い連結または縮小など)いくつかのまれなできごとで、コードもまた、それを書いてテストした通りのモードで実行されない場合があります。ここでは、コードを厳密に中立にするルールがあります。:
- strict としてコードを記述し、strict 専用のエラー (上記「新しいランタイムエラー」セクションにあるもの) がスローされないことを確認しています。
- 意味の違いから離れる
eval
: 何をやっているか知っている場合にのみ、それを使用してください。arguments
: 常に名前を経由して関数引数にアクセスしてください。または、使用している arguments オブジェクトのコピーを、下記を関数の最初の行に入れることで実行してください:
var args = Array.prototype.slice.call(arguments)
this
: 自ら生成したオブジェクトへ参照するときのみthis
を使用してください。