[JavaScript] ECMAScript5(JavaScript 1.8.5)におけるstrictモードのまとめ

2017 年 9 月 28 日 Categories: JavaScript | Tags:

使用法

全体への適用

スクリプト全体の最初に「”use strict”;」と書く。

かならず他の文の先頭でなければならない(コメントは先に書かれていてもOK)。

途中に書いた場合は、strictモードにならない。

そのため、複数のファイルを動的に読み込んだ場合(requireなど)、ファイルの先頭に「”use strict”;」の宣言があっても、先に別のファイルのスクリプトが実行されていれば非strictモードになる。

関数内のスコープへの適用

スコープの先頭に「”use strict”;」と書く。

strictモードの詳細

グローバル変数への代入禁止

"use strict";
sample = 17; // throws a ReferenceError

// グローバルオブジェクトのプロパティを指定する場合は可能。
window.sample = 17; // OK

代入文で暗黙的に失敗するのがエラーになる

本来代入できない場合に、かならずエラーを出すようになる。

"use strict";

// グローバル変数「NaN」への代入
NaN = 1; // throws an Error

// 書き込み禁止のプロパティへの代入
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // throws a TypeError

// getterしかないアクセサプロパティへの代入
var obj2 = { get x() { return 17; } };
obj2.x = 5; // throws a TypeError

// 新しいプロパティを追加できないオブジェクトへの代入
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // throws a TypeError

削除できないプロパティを削除しようとするとエラーが発生

"use strict";
delete Object.prototype; // throws a TypeError

非strict モードでは何も起きない。

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

"use strict";
var o = { p: 1, p: 2 }; // !!! syntax error

非strict モードでは、最後の指定が有効になる。

関数の引数名の重複禁止

function sum(a, a, c) // !!! syntax error
{
  "use strict";
  return a + b + c; // wrong if this code ran
}

非strict モードでは、最後の指定が有効になる。つまり、それ以前の引数にはargumentsでしかアクセスできない。

8進数表記の禁止

"use strict";
var sum = 015 + // !!! syntax error
          197 +
          142;

そもそも8進数表記は、ECMAScriptの仕様に含まれていない(ただし、現状全ブラウザが対応済みなので、動くことは動く)。

withの禁止

"use strict";
var x = 17;
with (obj) // !!! syntax error
{
  x;
}

eval()のスコープを限定

非strictモードでは、「eval(“var x;”)」 というコードは変数xを周囲のスコープに展開するが、それをさせない。

strictモードの内部で実行されるeval()内の処理は、strictモードで実行される。

スクリプト全体や関数が非strictモードでも、「eval(“‘use strict’; var x = 42; x”);」と書けば、eval()で実行する文字列の文はstrictモードとして扱われる。

変数のdeleteを禁止

var x;
delete x; // !!! syntax error

eval、argumentsの保護

evalおよびargumentsという名前に対して言語構文でのバインドや代入を不可にする。以下は、すべて構文エラーになる。

"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", "'use strict'; return 17;");

argumentsの値保持

argumentsが特殊なオブジェクトとして扱われる。関数呼び出し時の値をかならず保持する。

非strictモードでは仮引数の値を変更すると、対応するargumentsの各値も変わってしまう。

function f(a)
{
  "use strict";
  a = 42;
  return [a, arguments[0]]; // 非strictモードはarguments[0] === a
}
var pair = f(17);
console.assert(pair[0] === 42); // false
console.assert(pair[1] === 17); // true
var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
assert(x === 17); // true
assert(evalX === 42); // true

arguments.callee、arguments.callerの廃止

"use strict";
var f = function() { return arguments.callee; };
f(); // throws a TypeError

thisの非オブジェクト化

通常thisは、どんな場合でもその関数の親オブジェクト(ない場合はグローバルオブジェクト)を返すが、Function#call()やapply()で指定した値をかならず返すようになる。

// 以下はすべてtrue
"use strict";
function fun() { return this; }
console.assert(fun() === undefined);
console.assert(fun.call(2) === 2);
console.assert(fun.apply(null) === null);
console.assert(fun.call(undefined) === undefined);
console.assert(fun.bind(true)() === true);

予約語の追加

implements、interface、let、package、private、protected、public、static、yield。

変数名などには使えない。

functionを使った関数宣言をスクリプトのトップレベルと関数内のみに制限

ifやforの{}内でも駄目。

"use strict";
if (true)
{
  function f() { } // !!! syntax error
  f();
}
for (var i = 0; i < 5; i++)
{
  function f2() { } // !!! syntax error
  f2();
}
function baz() // OK
{
  function eit() { } // OK
}

【参考サイト】

Strict モード - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Strict_mode

【まとめ】

全体的に複雑で、あまりにも通常のJavaScriptと違いすぎて、かえって混乱の元になる。

個人的には、本当に必要な場合以外は使わないほうがいいと思う。