厳密モード

JavaScript には、通常の実行モードに加えて、厳密モードという 2 番目の実行モードがあります。名前が示すように、このモードではより厳密な JavaScript 構文が使用されます。

同じコードでも、通常モードと厳密モードでは実行結果が異なる場合があります。通常モードで実行できる一部のステートメントは、厳密モードでは実行できません。

設計の目的

初期の JavaScript 言語には多くの不合理な設計上の問題がありましたが、以前のコードとの互換性を保つために古い構文を変更することはできず、プログラマーが新しい構文を使用するようにガイドするために新しい構文を継続的に追加することしかできませんでした。

Strict モードは ES5 から標準に導入され、次の主な目的があります。

  • 不合理で不正確な構文を明示的に禁止し、JavaScript 言語の奇妙な動作を軽減します。
  • エラー報告の機会を追加し、コード実行の安全でない側面を排除し、コード実行の安全性を確保します。
  • コンパイラの効率が向上し、実行速度が向上します。
  • 将来の JavaScript 構文の新しいバージョンへの道を開きます。

つまり、厳密モードは、JavaScript のより合理的で、より安全で、より厳格な開発方向を反映しています。

メソッドを有効にする

strict モードに入る兆候は、文字列 use strict の行です。

'厳密を使用';

エンジンの古いバージョンでは、これを通常の文字列として扱い、無視します。新しいバージョンのエンジンは厳密モードに入ります。

Strict モードは、スクリプト全体に使用することも、個々の関数にのみ使用することもできます。

(1) スクリプト ファイル全体

「use strict」はスクリプト ファイルの最初の行に配置され、スクリプト全体が strict モードで実行されます。この行が最初の行ではない場合、その行は無効であり、スクリプト全体が通常モードで実行されます。 (厳密に言えば、実際の実行結果を生成するステートメントが前にない限り、「use strict」は空のセミコロンの直後やコメントの後に続くなど、最初の行にある必要はありません。)

<スクリプト>
  '厳密を使用';
  console.log('これは厳密モードです');
</script>

<スクリプト>
  console.log('これは通常モードです');
</script>

上記のコードでは、Web ページ ファイルに 2 つの JavaScript コードが含まれています。前者の <script> タグは厳密モードですが、後者はそうではありません。

以下のように use strict を記述した場合、コードの先頭から Strict モードが有効になっている必要があります。

<スクリプト>
  console.log('これは通常モードです');
  '厳密を使用';
</script>

(2)単機能

関数本体の最初の行に「use strict」を指定すると、関数全体が strict モードで実行されます。

関数 strict() {
  '厳密を使用';
  return 'これは厳密モードです';
}

関数 strict2() {
  '厳密を使用';
  関数 f() {
    return 'これも厳密モードです';
  }
  f() を返します。
}

関数 notStrict() {
  return 'これは通常モードです';
}

場合によっては、異なるスクリプトを 1 つのファイルに結合する必要があります。 1 つのスクリプトが厳密モードであり、別のスクリプトがそうでない場合、それらのマージが失敗する可能性があります。厳密モードのスクリプトが最初にある場合、マージされたスクリプトは厳密モードになります。通常モードのスクリプトが最初にある場合、マージされたスクリプトは通常モードになります。どちらの場合も、マージされた結果は正しくありません。現時点では、すぐに実行される匿名関数にスクリプト ファイル全体を配置することを検討できます。

(関数 () {
  '厳密を使用';
  // ここにいくつかのコード
})();

明示的なエラー報告

厳密モードでは、JavaScript の構文がより厳密になり、より多くの操作で明示的にエラーが報告されます。これらの操作の一部は、通常モードではエラーを報告せずに失敗します。

読み取り専用プロパティは書き込むことができません

厳密モードでは、文字列の「length」属性を設定するとエラーが報告されます。

'厳密を使用';
'abc'.length = 5;
// TypeError: 文字列 'abc' の読み取り専用プロパティ 'length' に割り当てることはできません

length は読み取り専用プロパティであり、strict モードでは書き込むことができないため、上記のコードはエラーを報告します。通常モードでは、長さ属性の変更は無効ですが、エラーは報告されません。

厳密モードでは、読み取り専用プロパティに値を割り当てたり、構成不可能なプロパティを削除するとエラーが発生します。

// 読み取り専用プロパティに値を割り当てるとエラーが報告されます
'厳密を使用';
Object.defineProperty({}, 'a', {
  値: 37、
  書き込み可能: false
});
obj.a = 123;
// TypeError: オブジェクト #<Object> の読み取り専用プロパティ 'a' に割り当てることはできません

// 構成不可能なプロパティを削除するとエラーが発生します
'厳密を使用';
var obj = Object.defineProperty({}, 'p', {
  値: 1、
  設定可能: false
});
obj.p を削除
// TypeError: #<Object> のプロパティ 'p' を削除できません

値セットのみを持つ属性は書き込み可能ではありません

厳密モードでは、ゲッターのみを持ちセッターを持たないプロパティに値を代入すると、エラーが報告されます。

'厳密を使用';
var obj = {
  get v() { 1 を返す }
};
obj.v = 2;
// Uncaught TypeError: ゲッターのみを持つ #<Object> のプロパティ v を設定できません

上記のコードでは、「obj.v」には値ゲッターのみがあり、値を代入するとエラーが報告されます。

拡張が禁止されているオブジェクトは拡張できません

厳密モードでは、拡張が禁止されているオブジェクトに新しい属性を追加するとエラーが発生します。

'厳密を使用';
var obj = {};
Object.preventExtensions(obj);
obj.v = 1;
// キャッチされない TypeError: プロパティ v を追加できません。オブジェクトは拡張可能ではありません

上記のコードでは、obj オブジェクトの拡張が禁止されており、属性を追加するとエラーが報告されます。

eval、引数を識別名として使用することはできません

strict モードでは、識別子名として eval または arguments を使用するとエラーが発生します。次のステートメントはエラーを報告します。

'厳密を使用';
var eval = 17;
var 引数 = 17;
var obj = { set p(arguments) { } };
try { } catch (引数) { }
関数 x(eval) { }
関数引数() { }
var y = 関数 eval() { };
var f = new Function('arguments', "'use strict'; return 17;");
// SyntaxError: 厳密モードでの予期しない eval または引数

関数には、重複する名前のパラメータを含めることはできません。

通常モードでは、関数に同じ名前の複数のパラメータがある場合、arguments[i] を使用してそれらを読み取ることができます。厳密モードでは、これは構文エラーです。

関数 f(a, a, b) {
  '厳密を使用';
  a + b を返します。
}
// キャッチされない SyntaxError: このコンテキストでは重複したパラメータ名は許可されません

8 進数の接頭辞 0 表記を無効にする

通常モードでは、整数の最初の桁が「0」の場合、それは 8 進数であることを意味します。たとえば、「0100」は 10 進数の 64 に相当します。厳密モードでは、この表現は禁止されています。整数の最初のビットが「0」の場合、エラーが報告されます。

'厳密を使用';
var n = 0100;
// Uncaught SyntaxError: 8 進リテラルは厳密モードでは許可されません。

セキュリティ対策の強化

厳密モードはセキュリティ保護を強化し、偶発的に発生する可能性のある文法エラーを防ぎます。

グローバル変数の明示的な宣言

通常モードでは、宣言されずに変数に値が割り当てられると、デフォルトでグローバル変数が使用されます。厳密モードではこの使用は禁止されており、グローバル変数は明示的に宣言する必要があります。

'厳密を使用';

v = 1; // エラー、v が宣言されていません。

for (i = 0; i < 2; i++) { // エラー、i が宣言されていません
  // ...
}

関数 f() {
  x = 123;
}
f() // エラー。宣言せずにグローバル変数を作成します

したがって、厳密モードでは、変数を最初に宣言してから使用する必要があります。

このキーワードがグローバル オブジェクトを指すことを禁止します

通常モードでは、関数内の「this」がグローバル オブジェクトを指す可能性があります。厳密モードでは、意図しないグローバル変数の作成を避けるために、この使用が禁止されています。

// 通常モード
関数 f() {
  console.log(この === ウィンドウ);
}
f() // true

// 厳密モード
関数 f() {
  '厳密を使用';
  console.log(this === 未定義);
}
f() // true

上記のコードでは、strict モードの関数本体内の thisunknown です。

この制限は、コンストラクターにとって特に役立ちます。コンストラクターを使用するときに、new を追加するのを忘れることがあります。このとき、this はグローバル オブジェクトを指していませんが、エラーが報告されます。

関数 f() {
  '厳密を使用';
  this.a = 1;
};

f(); // エラー、これは未定義です

strict モードでは、関数が (new で呼び出さずに) 直接呼び出される場合、関数内の this未定義 (未定義) を意味するため、callapply、および bind メソッドを使用できます。 to binding any 値は「this」にバインドされます。通常モードでは、this はグローバル オブジェクトを指します。ただし、バインドされた値が非オブジェクトの場合は、自動的にオブジェクトに変換されてからバインドされます。オブジェクトに変換できないため、無視されます。

// 通常モード
関数 fun() {
  これを返します。
}

fun() // ウィンドウ
fun.call(2) //番号 {2}
fun.call(true) // ブール値 {true}
fun.call(null) // ウィンドウ
fun.call(unknown) // ウィンドウ

// 厳密モード
'厳密を使用';
関数 fun() {
  これを返します。
}

fun() //未定義
fun.call(2) // 2
fun.call(true) // true
fun.call(null) // null
fun.call(未定義) // 未定義

上記のコードでは、任意のタイプの値を「this」にバインドできます。

fn.callee、fn.caller の使用は禁止されています

fn.callerfn.arguments は関数内で使用してはなりません。使用しないとエラーが報告されます。これは、関数内でコールスタックを取得できないことを意味します。

関数 f1() {
  '厳密を使用';
  f1.caller; // エラーレポート
  f1.arguments; // エラーレポート
}

f1();

argument.callee、arguments.caller の使用を無効にします

arguments.calleearguments.caller は、標準化されておらず、現在はキャンセルされている 2 つの歴史的な変数です。通常モードで呼び出しても効果はありませんが、エラーは報告されません。厳密モードでは、関数内で arguments.calleearguments.caller を使用するとエラーが報告されることが明確に規定されています。

'厳密を使用';
var f = 関数 () {
  引数を返します。呼び出し先。
};

f(); // エラーを報告する

変数の削除を無効にする

厳密モードでは変数を削除できません。「delete」コマンドを使用して変数を削除すると、エラーが報告されます。プロパティ記述オブジェクトの configurable プロパティが true に設定されている場合、オブジェクトのプロパティのみを delete コマンドで削除できます。

'厳密を使用';
変数x;
x を削除します // 構文エラー

var obj = Object.create(null, {
  ×: {
    値: 1、
    構成可能: true
  }
});
delete obj.x; // 削除に成功しました。

静的バインディング

JavaScript 言語の特徴は、「動的バインディング」が可能であることです。つまり、特定のプロパティやメソッドがどのオブジェクトに属するかは、コンパイル時ではなく実行時に決定されます。

厳密モードでは、動的バインディングにいくつかの制限が課されます。場合によっては、静的バインディングのみが許可されます。つまり、プロパティとメソッドがどのオブジェクトに属するかは、コンパイル段階で決定する必要があります。これにより、コンパイル効率が向上し、コードが読みやすくなり、予期せぬ事態が少なくなります。

具体的には、次のような側面が含まれます。

with ステートメントの使用を無効にする

厳密モードでは、「with」ステートメントを使用するとエラーが報告されます。これは、「with」ステートメントはコンパイル時に特定の属性がどのオブジェクトに属しているかを判断できないため、コンパイルの効果に影響を与えるためです。

'厳密を使用';
var v = 1;
var obj = {};

with (obj) {
  v = 2;
}
// キャッチされない SyntaxError: Strict モードのコードには with ステートメントが含まれていない可能性があります

評価スコープを作成する

通常モードでは、JavaScript 言語にはグローバル スコープと関数スコープという 2 つの変数スコープがあります。厳密モードでは 3 番目のスコープ、「eval」スコープが作成されます。

通常モードでは、「eval」ステートメントのスコープは、それがグローバル スコープにあるか関数スコープにあるかによって異なります。 strict モードでは、eval ステートメント自体がスコープであり、それが実行されるスコープ内で新しい変数を作成することはできなくなります。つまり、eval によって生成された変数は eval 内でのみ使用できます。 。

(関数 () {
  '厳密を使用';
  var x = 2;
  console.log(eval('var x = 5; x')) // 5
  console.log(x) // 2
})()

上記コードでは、eval文の内部は独立したスコープとなっているため、内部変数xが外部に漏洩することはありません。

「eval」ステートメントでも厳密モードを使用したい場合は、2 つの方法があることに注意してください。

// 方法 1
関数 f1(str){
  '厳密を使用';
  eval(str)を返します;
}
f1('undeclared_variable = 1'); // エラーレポート

// 方法 2
関数 f2(str){
  eval(str)を返します;
}
f2('"use strict";undeclared_variable = 1') // エラーレポート

上記 2 つの書き方では、eval は内部的に strict モードを使用します。

引数はパラメーターの変更を追跡しなくなりました

変数 arguments は関数のパラメータを表します。 strict モードでは、関数内のパラメーターの変更と「引数」の間の接続が切断され、この 2 つの間にリンケージ関係はなくなります。

関数 f(a) {
  a = 2;
  [a, 引数[0]] を返します。
}
f(1); //通常モードは [2, 2]

関数 f(a) {
  '厳密を使用';
  a = 2;
  [a, 引数[0]] を返します。
}
f(1); // 厳密モードは [2, 1]

上記のコードでは、関数のパラメータを変更しても arguments オブジェクトには反映されません。

JavaScript の次のバージョンへの移行

JavaScript 言語の次のバージョンは ECMAScript 6 であり、移行をスムーズにするために、厳密モードにはいくつかの ES6 構文が導入されています。

関数以外のコード ブロックでは関数を宣言してはなりません

ES6 ではブロックレベルのスコープが導入されます。新しいバージョンに準拠するために、ES5 の厳密モードでは、グローバル スコープまたは関数スコープでのみ関数を宣言できます。つまり、非関数コード ブロック内で関数を宣言することはできません。

'厳密を使用';
if (true) {
  function f1() { } // 構文エラー
}

for (var i = 0; i < 5; i++) {
  function f2() { } // 構文エラー
}

上記のコードは、「if」コード ブロックと「for」コード ブロックで関数を宣言しており、ES5 環境ではエラーが報告されます。

ES6 環境の場合、ES6 ではコード ブロックで関数を宣言できるため、上記のコードはエラーを報告しないことに注意してください。

予約語

将来の JavaScript の新しいバージョンに移行するために、strict モードではいくつかの新しい予約語 (implements、interface、let、package、private、protected、public、static、yield など) が追加されます。これらの単語を変数名として使用すると、エラーが発生します。

function package(protected) { // 構文エラー
  '厳密を使用';
  var 実装; // 構文エラー
}

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0