Strict Mode в ECMAScript. Полный справочник

от автора

По поводу строго режима существует множество информации. Но, к сожалению, очень мало статьей, покрывающих весь спектр особенностей строго режима. Поэтому, я решил составить свой справочник всех ограничений, исключений и отличий в исполнении «строгого» кода от «не строгого», в полном соответствии со спецификацией ECMA-262.

Что такое строгий режим

В спецификации ECMA-262 существует два понятия: «нестрогий режим» (non-strict mode) и «строгий режим» (strict mode). Каждая синтаксическая единица ECMAScript всегда исполняется с использованием синтаксиса и семантики того или другого режима. В отличие от нестрого, строгий режим исполнения накладывает ряд синтаксических ограничений, и, даже может исполняться иначе.

Какой код исполняется в строгом режиме, а какой в нестрогом

Для начала, стоит сказать, что, будучи примененным к данной синтаксической единице, отменить строгий режим в процессе исполнения для неё уже нельзя. Спецификацией не предусмотрено никаких директив для этого.

В строгом режим работает весь модульный код Module code и все части классового кода ClassDeclaration. Нет никакого способа отключить строгий режим в таком коде.

Весь остальной код, по умолчанию работает в нестрогом режиме, но его можно активировать посредством директивы «use strict» в Directive Prologue (строка, идущая до каких-либо других деклараций).

Ниже приведены возможные варианты включения строго режима в разных типах кода.

"use strict" // Global code теперь работает в строгом режиме  eval(`"use strict" // Eval code теперь работает в строгом режиме `);  function foo() {   "use strict"   // Function code теперь работает в строгом режиме, а сама функция теперь называется строгой }  new Function('a', 'b', ` "use strict";  // При использовании конструктора функции, строгий режим можно включить // в последнем аргументе конструктра, являющимся телом функции return a + b; `)

Однако, стоит учитывать, что строгий режим можно включить не в каждой функции. Если быть более конкретным, этот режим доступен только функциям с, так называемыми, простыми (IsSimpleParameterList) параметрами. Другими словами, не удасться включить строгий режим в следующих случаях

function foo(a = 1) {   // в случае использования значений по умолчанию     "use strict"; }  function foo(a, ...rest) {   // в случае использования rest-параметров    "use strict"; }  function foo({ a }) {   // в случае использования деструкции   "use strict"; }  // Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list

Так же, стоит учитывать, что код внутри строго режима будет распространять режим и на все вложенные синтаксические единицы.

"use strict"  eval(` // Eval code работает в строгом режиме `);  function foo() {   // как и Function code      function bar() {     // и вложенные функции тоже   } }

Зарезервированные слова

Зарезервированными (reserved words) считаются слова, которые нельзя использовать в качестве идентификатора. Не стоит путать зарезервированные слова с ключевыми (keywords). Многие из ключевых слов являются зарезервированными, но не все. Также, есть слова, которые являются зарезервированными только в конкретном контексте (например, await является зарезервированным только внутри асинхронной функции или модуля).

Конкретно в strict mode зарезервированными считаются следующие слова:

implementsinterfaceletpackageprivateprotectedpublicstaticyield

Это значит, что их нельзя использовать в качестве индентификаторов

"use strict"  let implements = "";  // Uncaught SyntaxError: Unexpected strict mode reserved word

Восьмеричный литерал

В строгом режиме его использование запрещено. На всякий случай напомню, восьмеричный литерал начинается с символа 0 (можно несколько) и далее цифры  0, 1, 2, 3, 4, 5, 6, 7

"use strict" 01  // Uncaught SyntaxError: Octal literals are not allowed in strict mode.

Объявление переменных и свойства объекта

В нестрогом режиме, подобная конструкция приводит к тому, что в глобальном контексте объявляется переменная, которой присваивается значение

a = "a"  // Window { //   ... //   a: "a" // }

В строгом режиме такой код запрещен

"use strict"  a = "a";  // Uncaught ReferenceError: a is not defined

Также, в строгом режиме учитывается атрибут { [[Writable]]: false } свойств объекта

"use strict"  const object = {};  Object.defineProperty(object, "a", {     value: "a",     writable: false, });  object.a = "b";  // Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'

Аксессор { [[Set]]: undefined } свойства объекта тоже приведет к ошибке

"use strict"  const object = {};  Object.defineProperty(object, "a", {     set: undefined, });  object.a = "b";  // Uncaught TypeError: Cannot set property a of #<Object> which has only a getter

Аналогично, нельзя и добавлять новые свойства нерасширяемому объекту

"use strict"  const object = {}; Object.preventExtensions(object);  // Нерасширяемыми, также, считаются запечатанные объекты // Object.seal(object); // // и замороженные // Object.freeze(object)  object.a = "a";  // Uncaught TypeError: Cannot add property a, object is not extensible

eval и arguments

Если переменная имеет идентифактор, соответствующий какому-либо из этих двух ключевых слов, она не может осуществлять операции присваивания и унарного обновления

"use strict"  // Каждая строчка ниже приведет к ошибке // Uncaught SyntaxError: Unexpected eval or arguments in strict mode  let eval = 1;  let argumnets = 1;  eval++;  arguments--;  --eval;  ++arguments;

Так же, нельзя использовать использовать эти слова в качестве идентификатора функции

"use strict"  function eval() {} function arguments() {}  // Uncaught SyntaxError: Unexpected eval or arguments in strict mode

и использовать их в качестве параметра блока catch

"use strict"  try {} catch (arguments) {}  // Uncaught SyntaxError: Unexpected eval or arguments in strict mode

arguments

В строгой функции (когда строгий режим включен внутри самой фукнции) объект arguments переопределять нельзя

function foo() {     "use strict"     arguments = []; }  foo(1);  // Uncaught SyntaxError: Unexpected eval or arguments in strict mode

В ранних версиях JavaScript небыло никакой другой возможности получить ссылку из функции на саму себя, кроме как через arguments.callee. Сейчас такой проблемы нет и можно спокойно использовать идентификатор функции внутрии неё самой, а использовать arguments.callee в строгих функциях — запрещено.

function foo() {     "use strict"     console.log(arguments.callee); }  foo();  // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

Вместо этого стоит использовать идентификатор самой функции

function foo() {     "use strict"     console.log(foo); }  foo();  // ƒ foo() { //    "use strict" //    console.log(foo); // }

Кроме этого, важный неочевидный момент — в строгой функции, элементы arguments не имеют связанности с прямыми аргументами

function foo(a) {     "use strict"     arguments[0] = 2;          console.log(a, arguments[0]) }  foo(1);  // 1 2

Здесь, переопределив элемент arguments, сам аргумент a остался неизменным.

В нестрогом режиме подобный код поведет себя иначе

function foo(a) {     arguments[0] = 2;          console.log(a, arguments[0]) }  foo(1);  // 2 2

Здесь, переопределение элемента arguments приведет и к переопределению прямого аргумента.

Помимо этого, у строгой функции нельзя каким-либо образом изменить или расширить ссылку arguments и нестандартизированную ссылку caller, даже если глобальное окружение, само по себе, в строгом режиме не находится

function foo() {     "use strict" }  foo.caller = null foo.arguments = null  foo();  // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

eval

Код eval, в строгом режиме не может объявлять переменные и функции в VariableEnvironment вызывающего окружения. Вместо этого, создается новый VariableEnvironment, доступный только внутри eval.

eval(`"use strict"      var a = 1`);  console.log(a);  // Uncaught ReferenceError: a is not defined

В нестрогом режиме переменная объявится в глобальном окружении

eval("var a = 1");  console.log(a);  // 1

На всякий случай уточню, речь идет именно о VariableEnvironment (переменная объявляется с помощью var). В случае с letconst и class, переменная сразу объявляется в LexicalEnvironment и, соответственно, будет доступна только внутри этого окружения, вне зависимости от режима.

this

В нестрогом режиме, если ссылка на контекст пустая (undefined или null), она автоматически меняется на ссылку глобального контекста

function foo() {     console.log(this); }  foo();  // Window { ... }

В строгом же режиме, ссылка меняться не будет и останется пустой

"use strict"  function foo() {     console.log(this); }  foo();  // undefined

То же справедливо и при использовании Function.prototype.apply и Function.prototype.call

"use strict"  function foo() {     console.log(this); }  foo.apply(null);  // null

delete

В строгом режиме нельзя применять оператор delete к прямым ссылкам на переменную, агрумент функции и имя функции

"use strict"  var a = 1; delete a;  function foo(arg) {     delete arg; }  delete foo;  // Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.

Вообще, этот оператор стоит применять только к свойствам объекта, например так

"use strict"  const object = {     a: 1, };  delete object.a;  // <- true

Однако, если свойство объекта имеет атрибут { [[Configurable]]: false } или каким либо другим образом защищено от удаления, в строгом режиме удалить его также не получится

"use strict"  const object = {};  Object.defineProperty(object, "a", { configurable: false });  delete object.a;  // Uncaught TypeError: Cannot delete property 'a' of #<Object>  delete Object.prototype  // Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }

with

В строгом режиме нельзя использовать выражение with. Любые попытки его применить вызовут синтаксическую ошибку

"use strict"  const object = {};  with (object) {}  // Uncaught SyntaxError: Strict mode code may not include a with statement

Дубликаты параметров функций

В строгой функции, попытка дублирования названий параметров приведет к синтаксической ошибке

function foo(a, a) {     "use strict" }  foo();  // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

Аналогично и при использовании конструктора функции

const foo = new Function('a', 'a', ` "use strict"; `);  foo();  // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

В то время как в нестрогой функции дублирование возможно

function foo(a, a) {     console.log(a); }  foo(1, 2);  // 2

Эту и другие мои статьи, так же, читайте в моем канале:

RU: https://t.me/frontend_almanac_ru
EN: https://t.me/frontend_almanac


ссылка на оригинал статьи https://habr.com/ru/articles/791978/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *