Spec-Zone.ru › JavaScript

Строгий режим

Примечание: Иногда обычный, нестрогий режим называют "небрежным режимом" (sloppy mode). Это неофициальный термин, но имейте его в виду на всякий случай.

Строгий режим JavaScript — это способ включить ограниченный вариант JavaScript, тем самым неявно исключая "небрежный режим". Строгий режим — это не просто подмножество: он намеренно имеет иную семантику, чем обычный код. Код в строгом режиме и код в нестрогом режиме могут сосуществовать, поэтому скрипты могут постепенно переходить в строгий режим.

Строгий режим вносит несколько изменений в обычную семантику JavaScript:

  1. Устраняет некоторые "тихие" ошибки JavaScript, преобразуя их в выбрасываемые исключения.
  2. Исправляет ошибки, которые затрудняют оптимизацию движками JavaScript: код в строгом режиме иногда может выполняться быстрее, чем идентичный код вне строгого режима.
  3. Запрещает некоторую синтаксическую конструкцию, которая, вероятно, будет определена в будущих версиях ECMAScript.

Применение строгого режима

Строгий режим применяется ко всему скрипту или к отдельным функциям. Он не применяется к блочным операторам, заключенным в {} фигурные скобки; попытка применить его в таких контекстах ничего не дает. Код, вызываемый через eval, код Function, атрибуты обработчика событий event handler, строки, передаваемые в setTimeout(), и связанные с ними функции являются либо телами функций, либо целыми скриптами, и применение строгого режима к ним работает как ожидается.

Строгий режим для скриптов

Чтобы применить строгий режим ко всему скрипту, поместите точно оператор "use strict"; (или 'use strict';) перед любыми другими операторами.

// Whole-script strict mode syntax
"use strict";
const v = "Hi! I'm a strict mode script!";

Строгий режим для функций

Аналогично, чтобы применить строгий режим к функции, поместите точно оператор "use strict"; (или 'use strict';) в тело функции перед любыми другими операторами.

function myStrictFunction() {
  // Function-level strict mode syntax
  "use strict";
  function nested() {
    return "And so am I!";
  }
  return `Hi! I'm a strict mode function! ${nested()}`;
}
function myNotStrictFunction() {
  return "I'm not strict.";
}

Директива "use strict" может быть применена только к телам функций с простыми параметрами. Использование "use strict" в функциях с rest, по умолчанию или деструктурированными параметрами является синтаксической ошибкой.

function sum(a = 1, b = 2) {
  // SyntaxError: "use strict" not allowed in function with default parameter
  "use strict";
  return a + b;
}

Строгий режим для модулей

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

function myStrictFunction() {
  // because this is a module, I'm strict by default
}
export default myStrictFunction;

Строгий режим для классов

Все части тела класса являются кодом строгого режима, включая как объявления классов, так и выражения классов.

class C1 {
  // All code here is evaluated in strict mode
  test() {
    delete Object.prototype;
  }
}
new C1().test(); // TypeError, because test() is in strict mode

const C2 = class {
  // All code here is evaluated in strict mode
};

// Code here may not be in strict mode
delete Object.prototype; // Will not throw error

Изменения в строгом режиме

Строгий режим изменяет как синтаксис, так и поведение во время выполнения. Изменения обычно попадают в следующие категории:

  • изменения, преобразующие ошибки в исключения (в виде синтаксических ошибок или во время выполнения)
  • изменения, упрощающие разрешение ссылок на переменные
  • изменения, упрощающие eval и arguments
  • изменения, облегчающие написание "безопасного" JavaScript
  • изменения, предвосхищающие эволюцию ECMAScript в будущем.

Превращение ошибок в исключения

Строгий режим преобразует некоторые ранее допустимые ошибки в исключения. JavaScript был разработан, чтобы быть простым для начинающих разработчиков, и иногда он дает операциям, которые должны быть ошибками, семантику без ошибок. Иногда это исправляет непосредственную проблему, но иногда создает более серьезные проблемы в будущем. Строгий режим обрабатывает эти ошибки как исключения, чтобы они были обнаружены и оперативно исправлены.

Присваивание необъявленным переменным

Строгий режим делает невозможным случайное создание глобальных переменных. В нестрогом режиме опечатка в имени переменной при присваивании создает новое свойство в глобальном объекте и продолжает "работать". Присваивание, которое случайно создало бы глобальные переменные, в строгом режиме выбрасывает исключение:

"use strict";
let mistypeVariable;

// Assuming no global variable mistypeVarible exists
// this line throws a ReferenceError due to the
// misspelling of "mistypeVariable" (lack of an "a")
mistypeVarible = 17;

Неудачное присваивание свойствам объекта

В строгом режиме определенные присваивания выбрасывают исключения вместо тихого сбоя. Есть три способа неудачного присваивания свойства:

  • присваивание свойству данных, которое нельзя записывать
  • присваивание свойству доступа, которое доступно только для чтения (getter-only)
  • присваивание нового свойства нерасширяемому объекту

Например, NaN — это глобальная переменная, которую нельзя изменять. В нестрогом режиме присваивание NaN ничего не делает; разработчик не получает никакой обратной связи о сбое. В строгом режиме присваивание NaN выбрасывает исключение.

"use strict";

// Assignment to a non-writable global
undefined = 5; // TypeError
Infinity = 5; // TypeError

// Assignment to a non-writable property
const obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // TypeError

// Assignment to a getter-only property
const obj2 = {
  get x() {
    return 17;
  },
};
obj2.x = 5; // TypeError

// Assignment to a new property on a non-extensible object
const fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // TypeError

Неудачное удаление свойств объекта

Попытки удалить свойство, которое нельзя конфигурировать или которое нельзя удалить по другой причине (например, оно перехвачено обработчиком deleteProperty прокси, который возвращает false), в строгом режиме выбрасывают исключение (тогда как ранее попытка ничего не давала):

"use strict";
delete Object.prototype; // TypeError
delete [].length; // TypeError

Строгий режим также запрещает удаление простых имен. delete name в строгом режиме является синтаксической ошибкой:

"use strict";

var x;
delete x; // syntax error

Если имя является конфигурируемым глобальным свойством, предварите его globalThis, чтобы удалить.

"use strict";

delete globalThis.x;

Дублирующиеся имена параметров

Строгий режим требует, чтобы имена параметров функций были уникальными. В нестрогом режиме последний дублирующийся аргумент скрывает предыдущие аргументы с тем же именем. Предыдущие аргументы остаются доступными через arguments, поэтому они не полностью недоступны. Тем не менее, такое сокрытие малоосмысленно и, вероятно, нежелательно (оно может скрыть опечатку, например), поэтому в строгом режиме дублирующиеся имена аргументов являются синтаксической ошибкой:

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

Также является синтаксической ошибкой в нестрогом режиме иметь дублирующиеся имена параметров, если функция имеет параметр по умолчанию, rest-параметр или деструктурированный параметр.

Устаревшие восьмеричные литералы

Строгий режим запрещает восьмеричный литерал, предваряемый 0-. В нестрогом режиме число, начинающееся с 0, такое как 0644, интерпретируется как восьмеричное число (0644 === 420), если все цифры меньше 8. Начинающие разработчики иногда полагают, что префикс из нуля не имеет семантического значения, поэтому они могут использовать его как выравнивающее устройство — но это меняет значение числа! Синтаксис с ведущим нулем для восьмеричной системы редко полезен и может быть использован по ошибке, поэтому строгий режим делает его синтаксической ошибкой:

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

Стандартизированный способ обозначения восьмеричных литералов — через префикс 0o. Например:

const sumWithOctal = 0o10 + 8;
console.log(sumWithOctal); // 16

Восьмеричные escape-последовательности, такие как "\45", что равно "%", могут использоваться для представления символов по расширенным-ASCII кодам символов в восьмеричной системе. В строгом режиме это синтаксическая ошибка. Более формально, запрещено иметь \, за которым следует любая десятичная цифра, кроме 0, или \0, за которым следует десятичная цифра; например \9 и \07.

Установка свойств примитивных значений

Строгий режим запрещает установку свойств примитивных значений primitive. Доступ к свойству примитивного типа неявно создает обернутый объект, который нельзя наблюдать, поэтому в нестрогом режиме установка свойств игнорируется (no-op). В строгом режиме выбрасывается TypeError.

"use strict";

false.true = ""; // TypeError
(14).sailing = "home"; // TypeError
"with".you = "far away"; // TypeError

Дублирующиеся имена свойств

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

"use strict";
const o = { p: 1, p: 2 }; // syntax error prior to ECMAScript 2015

Примечание: Превращение кода, который раньше вызывал ошибки, в код без ошибок всегда считается обратносовместимым. Это хорошая часть языка, когда он строго относится к генерации исключений: это оставляет место для будущих семантических изменений.

Упрощение управления областями видимости

Строгий режим упрощает сопоставление имен переменных с конкретными определениями переменных в коде. Многие оптимизации компилятора полагаются на возможность сказать, что переменная X хранится в этом месте: это критически важно для полной оптимизации кода JavaScript. JavaScript иногда делает это базовое сопоставление имени с определением переменной в коде невозможным до выполнения во время выполнения. Строгий режим устраняет большинство случаев, когда это происходит, поэтому компилятор может лучше оптимизировать код строгого режима.

Удаление оператора with

Строгий режим запрещает with. Проблема с with заключается в том, что любое имя внутри блока может сопоставляться либо со свойством объекта, переданного ему, либо с переменной во внешней (или даже глобальной) области видимости во время выполнения; заранее невозможно узнать, с чем именно.

Строгий режим делает with синтаксической ошибкой, поэтому нет никакой возможности, чтобы имя в with ссылалось на неизвестное место во время выполнения:

"use strict";
const x = 17;
with (obj) {
  // Syntax error
  // If this weren't strict mode, would this be const x, or
  // would it instead be obj.x? It's impossible in general
  // to say without running the code, so the name can't be
  // optimized.
  x;
}

Альтернатива присваиванию объекта короткому имени переменной, а затем доступу к соответствующему свойству этой переменной, готова заменить with.

Eval, не "протекающий" в область видимости

В строгом режиме eval не вводит новые переменные в окружающую область видимости. В нестрогом режиме eval("var x;") вводит переменную x в окружающую функцию или глобальную область видимости. Это означает, что, в общем случае, в функции, содержащей вызов eval, каждое имя, не ссылающееся на аргумент или локальную переменную, должно быть сопоставлено с конкретным определением во время выполнения (поскольку этот eval мог ввести новую переменную, которая скрывала бы внешнюю переменную). В строгом режиме eval создает переменные только для выполняемого кода, поэтому eval не может повлиять на то, ссылается ли имя на внешнюю переменную или на некоторую локальную переменную:

var x = 17;
var evalX = eval("'use strict'; var x = 42; x;");
console.assert(x === 17);
console.assert(evalX === 42);

Является ли строка, переданная в eval(), вычисляемой в строгом режиме, зависит от того, как вызывается eval() (прямой eval или косвенный eval).

Функциональные объявления с блочной областью видимости

Спецификация языка JavaScript с самого начала не допускала объявлений функций, вложенных в блочные операторы. Однако это было настолько интуитивно, что большинство браузеров реализовали это как расширенную грамматику. К сожалению, семантика реализаций разошлась, и спецификации языка стало невозможно примирить все реализации. Поэтому функциональные объявления с блочной областью видимости явно указаны только в строгом режиме (тогда как ранее они были запрещены в строгом режиме), в то время как поведение нестрогого режима остается различным среди браузеров.

Упрощение eval и arguments

Строгий режим делает arguments и eval менее мистически волшебными. Оба включают значительное количество магического поведения в нестрогом режиме: eval для добавления или удаления привязок и изменения значений привязок, а также arguments для синхронизации именованных аргументов с его индексированными свойствами. Строгий режим делает большие шаги к тому, чтобы рассматривать eval и arguments как ключевые слова.

Запрет привязки или присваивания eval и arguments

Имена eval и arguments не могут быть привязаны или назначены в синтаксисе языка. Все попытки сделать это являются синтаксическими ошибками:

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

Нет синхронизации между параметрами и индексами аргументов

Код строгого режима не синхронизирует индексы объекта arguments с каждой привязкой параметра. В функции нестрогого режима, первый аргумент которой — arg, установка arg также устанавливает arguments[0], и наоборот (если аргументы не были предоставлены или arguments[0] удален). Объекты arguments для функций строгого режима хранят исходные аргументы при вызове функции. arguments[i] не отслеживает значение соответствующего именованного аргумента, так же как и именованный аргумент не отслеживает значение в соответствующем arguments[i].

function f(a) {
  "use strict";
  a = 42;
  return [a, arguments[0]];
}
const pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17);

"Безопасность" JavaScript

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

Отсутствие подстановки this

Значение, передаваемое как this функции в строгом режиме, не принудительно преобразуется в объект (также известный как "boxing"). Для функции нестрогого режима this всегда является объектом: либо предоставленный объект, если вызван с this, имеющим объектное значение; либо упакованное значение this, если вызван с примитивным значением в качестве this; либо глобальный объект, если вызван с undefined или null в качестве this. (Используйте call, apply или bind, чтобы указать конкретный this.) Автоматическое boxing — это не только накладные расходы на производительность, но и раскрытие глобального объекта в браузерах является угрозой безопасности, поскольку глобальный объект предоставляет доступ к функциональности, которую должны ограничивать "безопасные" среды JavaScript. Таким образом, для функции строгого режима указанный this не упаковывается в объект, а если не указан, то this является undefined вместо globalThis:

"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);

Удаление свойств для обхода стека

В строгом режиме больше не возможно "обходить" стек JavaScript. Многие реализации ранее предоставляли некоторые расширенные функции, которые позволяли обнаруживать вышестоящий вызывающий функцию. Когда функция fun находится в процессе вызова, fun.caller — это функция, которая последней вызвала fun, а fun.arguments — это arguments для этого вызова fun. Оба расширения проблематичны для "безопасного" JavaScript, поскольку они позволяют "защищенному" коду получать доступ к "привилегированным" функциям и их (потенциально незащищенным) аргументам. Если fun находится в строгом режиме, оба fun.caller и fun.arguments являются не удаляемыми свойствами, которые выбрасывают исключение при установке или получении:

function restricted() {
  "use strict";
  restricted.caller; // throws a TypeError
  restricted.arguments; // throws a TypeError
}
function privilegedInvoker() {
  return restricted();
}
privilegedInvoker();

Аналогично, arguments.callee больше не поддерживается. В нестрогом режиме arguments.callee ссылается на заключающую функцию. Этот вариант использования слаб: назовите заключающую функцию! Более того, arguments.callee значительно препятствует оптимизациям, таким как встраивание функций, поскольку необходимо предоставить ссылку на невстроенную функцию, если доступ к arguments.callee осуществляется. arguments.callee для функций строгого режима — это не удаляемое свойство, которое выбрасывает исключение при установке или получении:

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

Защита JavaScript от будущих изменений

Дополнительные зарезервированные слова

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

  • implements
  • interface
  • let
  • package
  • private
  • protected
  • public
  • static
  • yield

Переход к строгому режиму

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

Вы можете мигрировать кодовую базу в строгий режим, сначала добавив "use strict" к части исходного кода, а затем исправив все ошибки выполнения, следя за семантическими различиями.

Синтаксические ошибки

При добавлении 'use strict'; следующие случаи вызовут SyntaxError до выполнения скрипта:

  • Синтаксис восьмеричных чисел const n = 023;
  • Оператор with
  • Использование delete для имени переменной delete myVariable;
  • Использование eval или arguments в качестве имени переменной или аргумента функции
  • Использование одного из новых зарезервированных ключевых слов (в предвидении будущих возможностей языка): implements, interface, let, package, private, protected, public, static и yield
  • Объявление двух параметров функции с одинаковым именем function f(a, b, b) {}
  • Двойное объявление одного и того же имени свойства в литерале объекта {a: 1, b: 3, a: 7}. Это ограничение было позже снято (bug 1041128).

Эти ошибки хороши, потому что они выявляют явные ошибки или плохие практики. Они возникают до запуска кода, поэтому их легко обнаружить, пока код анализируется средой выполнения.

Новые ошибки времени выполнения

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

  • Присваивание необъявленной переменной выбрасывает ReferenceError. Раньше это устанавливало свойство в глобальном объекте, что редко является ожидаемым эффектом. Если вы действительно хотите присвоить значение глобальному объекту, явно присвойте его как свойство globalThis.
  • Неудачное присваивание свойству объекта (например, оно только для чтения) выбрасывает TypeError. В нестрогом режиме это безмолвно сбоило.
  • Удаление свойства, которое нельзя удалить, выбрасывает TypeError. В нестрогом режиме это безмолвно сбоило.
  • Доступ к arguments.callee, strictFunction.caller или strictFunction.arguments выбрасывает TypeError, если функция находится в строгом режиме. Если вы используете arguments.callee для рекурсивного вызова функции, вы можете вместо этого использовать именованное выражение функции.

Семантические различия

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

this
В нестрогом режиме вызовы функций, такие как f(), передавали бы глобальный объект как значение this. В строгом режиме теперь это undefined. Когда функция вызывалась с call или apply, если значение было примитивным, оно упаковывалось в объект (или глобальный объект для undefined и null). В строгом режиме значение передается напрямую без преобразования или замены.
arguments
В нестрогом режиме изменение значения в объекте arguments изменяет соответствующий именованный аргумент. Это усложняло оптимизацию для движка JavaScript и делало код труднее читать/понимать. В строгом режиме объект arguments создается и инициализируется теми же значениями, что и именованные аргументы, но изменения либо в объекте arguments, либо в именованных аргументах не отражаются друг на друге.
eval
В коде строгого режима eval не создает новую переменную в области видимости, из которой он был вызван. Также, конечно, в строгом режиме строка вычисляется с использованием правил строгого режима. Необходимо провести тщательное тестирование, чтобы убедиться, что ничего не сломалось. Неиспользование eval, если он вам действительно не нужен, может быть другим прагматичным решением.
Функциональные объявления с блочной областью видимости
В нестрогом режиме объявление функции внутри блока может быть видимым вне блока и даже вызываемым. В строгом режиме объявление функции внутри блока видимо только внутри блока.

Спецификации

Спецификация
ECMAScript® 2027 Language Specification

См. также

  • Руководство по модулям JavaScript
  • Лексическая грамматика

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API