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 зарезервированными считаются следующие слова:
implements, interface, let, package, private, protected, public, static, yield
Это значит, что их нельзя использовать в качестве индентификаторов
"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 extensibleeval и 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 modearguments
В строгой функции (когда строгий режим включен внутри самой фукнции) объект 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 themeval
Код 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). В случае с let, const и 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);
// nulldelete
В строгом режиме нельзя применять оператор 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);
// 2EN - https://t.me/frontend_almanac
RU - https://t.me/frontend_almanac_ru
English version: https://blog.frontend-almanac.com/strict-mode