Источник: Nuances of Programming
Строгий режим — это важная часть современного JavaScript. Он позволяет использовать ограниченный синтаксис JavaScript.
Семантика строгого режима отличается от старого “неаккуратного режима” JavaScript с его слабым синтаксисом и “замалчиваемыми” ошибками в коде — такие ошибки игнорируются, и код может запускаться с неожиданными результатами.
Строгий режим вносит несколько изменений в семантику JavaScript. Он заменяет исключениями “замалчиваемые” в обычном режиме ошибки, поэтому с этими ошибками код не запускается.
Он также исправляет ошибки, мешающие движкам JavaScript производить оптимизацию, и запрещает функции, которые могут быть определены в будущих версиях JavaScript.
Строгий режим применим как к отдельным функциям, так и к целому скрипту. Его нельзя применять только к операторам и другим блокам, заключенным в фигурные скобки. Чтобы скрипт использовал строгий режим, добавляем оператор "use strict" или 'use strict' в начало скрипта перед всеми остальными операторами.
Будьте внимательны: при объединении со скриптами, использующими строгий режим, скрипты, которые его не используют, начинают его использовать, и наоборот, поэтому объединять скрипты с разными режимами не стоит.
Чтобы применить строгий режим к функциям, нужно добавить оператор "use strict" или 'use strict' внутрь функции перед всеми остальными операторами. Он применяется ко всему, что находится внутри, включая вложенные функции.
Например:
const strictFunction = ()=>{
'use strict';
const nestedFunction = ()=>{
// эта функция также использует строгий режим
}
}
В модулях JavaScript, представленных в ES2015, строгий режим применяется автоматически, поэтому его не нужно включать операторами.
Изменения в строгом режиме
Строгий режим изменяет и синтаксис, и поведение кода во время выполнения. Наиболее важные изменения:
- преобразование ранее допустимых ошибок в ошибки синтаксиса или ошибки выполнения;
- изменения, упрощающие вычисление конкретных переменных;
- изменения, упрощающие функцию eval и объект arguments;
- изменения, которые будут применены в будущей спецификации ES.
Преобразование допустимых ошибок в недопустимые
В нестрогом режиме на некоторые допустимые ошибки JS никак не реагировал. Строгий режим ограничивает использование ошибочного синтаксиса и не позволяет коду запускаться с ошибками.
Он затрудняет создание глобальных переменных, не позволяя объявлять переменные с помощью var, let или const. Например, код ниже выдаст ReferenceError:
'use strict';
badVariable = 1;
Код не запустится в строгом режиме — глобальную переменную badVariable можно создать только при выключенном строгом режиме, который нужен для предотвращения случайного создания глобальных переменных.
Теперь любой код с этой ранее допустимой ошибкой выдаст исключение. Это распространяется и на некорректный синтаксис, который игнорировался ранее.
Например, в строгом режиме мы не можем присваивать значения переменным только для чтения: arguments, NaNили eval. Любое присвоение значений защищенным от записи глобальным переменным, свойствам только для геттеров и свойствам нерасширяемых объектов в строгом режиме выдаст исключение.
Ниже несколько примеров неудачного синтаксиса:
Все примеры выше выдадут TypeError. undefined и Infinity — глобальные переменные, защищенные от записи, obj — защищенное от записи свойство.
Свойство foo obj2 — единственное свойство геттера, и поэтому не может быть задано. fixedObj был защищен от добавления новых свойств методом Object.preventExtensions.
Кроме того, при попытке удалить неудаляемые свойства появится исключение TypeError, например:
'use strict';
delete Array.prototype
В строгом режиме имена параметров функции должны быть уникальны. В обычном режиме, если у двух параметров одинаковое имя, определенное позже имя будет принято как значение параметра при передаче аргументов. Поэтому следующий пример выдаст синтаксическую ошибку:
const multiply = (x, x, y) => x*x*y;
В строгом режиме восьмеричная запись чисел также не разрешена. Она не является частью спецификации, но поддерживается в браузерах добавлением 0 к восьмеричным числам. Это сбивает разработчиков с толку, так как некоторые думают, что 0 перед числом ничего не значит. Как следствие, строгий режим не разрешает этот синтаксис и выдает ошибку.
Строгий режим предотвращает использование синтаксиса, усложняющего оптимизацию. Ему нужно знать, что переменная действительно хранится в том месте, где он думает, до того, как производить оптимизацию.
Один из примеров — это оператор with. При его использовании интерпретатор JavaScript не знает, на какую переменную или свойство вы ссылаетесь, поскольку переменная с тем же именем может быть внутри или снаружи оператора with.
Приведем пример:
let x = 1;
with (obj) {
x;
}
JavaScript не будет знать, x внутри оператора with ссылается на переменную x или свойство obj, obj.x.
Следовательно, расположение x в памяти неоднозначно. Таким образом, строгий режим запрещает использование оператора with. При включенном строгом режиме пример ниже выдаст ошибку:
'use strict';
let x = 1;
with (obj) {
x;
}
Следующая вещь, запрещенная в строгом режиме — это объявление переменных внутри оператора eval.
Например, без строгого режима eval('let x') объявит переменную x внутри кода. Это позволяет программистам прятать объявление переменных в строках, что может блокировать объявление той же переменной вне оператора eval.
Чтобы предотвратить это, строгий режим не позволяет объявлять переменные в аргументе строки, который мы передаем внутрь оператора eval.
Строгий режим также запрещает удаление простых имен переменных, поэтому код ниже выдаст синтаксическую ошибку:
'use strict';
let x;
delete x;
Запрет неверного синтаксиса
Неверный синтаксис метода eval и объекта argument не разрешен в строгом режиме.
Например, им нельзя задать новые значения или использовать их как имена переменных, функций или параметров функций.
Вот пример неверного использования eval и argument:
Строгий режим не разрешает создавать псевдоним для объекта arguments и задавать с ним новые значения.
Без строгого режима, если первый параметр функции — a, тогда установка a также задает arguments[0]. В строгом режиме у объекта arguments всегда будет список аргументов, с которыми вызывается функция.
Например, если у нас есть:
Тогда мы должны увидеть [2,1] в журнале, потому что установка значения 2 в a не задает его в arguments[0].
Оптимизация производительности
Кроме того, больше нет поддержки arguments.callee. Без строгого режима arguments.callee возвращает имя функции с arguments.callee внутри.
Это мешает оптимизациям, например, встроенным функциям, потому что arguments.callee требует, чтобы при его вызове была доступна ссылка на невстроенную функцию. Поэтому теперь в строгом режиме arguments.callee вызывает TypeError.
В строгом режиме значение this не приводится к объекту. Если this функции связан с call, apply или bind с любыми необъектными типами, такими как примитивные типы undefined, null, number, boolean и так далее, они будут принудительно приведены к объекту.
Если контекст this переключается в необъектный режим, его место занимает глобальный объект window. Это означает, что глобальный объект открыт для функции, вызываемой this, связанным с необъектным типом.
Например, если мы запустим код ниже:
Все журналы консоли будут иметь значение true, так как this внутри функции не преобразуется автоматически в глобальный объект window, когда this меняется на что-то, имеющее необъектный тип.
Исправления безопасности
В строгом режиме caller и arguments не публичные, так как caller может отображать функцию, вызывающую другую функцию, к которой обращается это свойство caller.
В arguments есть аргументы, передаваемые при вызове функции. Например, если у нас есть функция fn, тогда через fn.caller мы можем увидеть аргументы, которые были переданы в fn, когда был совершен ее вызов.
Это создает потенциальную дыру в безопасности, которая устраняется запретом доступа к этим двум свойствам функции.
В строгом режиме в примере выше мы не сможем получить доступ к secretFunction.caller и secretFunction.arguments, так как люди могут использовать их для получения стека функций. При выполнении код выдаст TypeError.
Идентификаторы, которые станут ключевыми словами в будущих версиях JavaScript, нельзя использовать для именования переменных или свойств объектов. В ES2015 или позже следующие ключевые слова стали зарезервированными, и их нельзя использовать для определения идентификаторов в коде: implements, interface, let, package, private, protected, public, static и yield.
Строгий режим был стандартом несколько лет. Обычно браузеры его поддерживают. Проблемы могут возникнуть только в старых браузерах, таких как Internet Explorer.
У других браузеров не должно быть проблем в работе со строгим режимом, поэтому его стоит использовать для предотвращения перечисленных выше ошибок.
Читайте также:
Читайте нас в телеграмме и vk
Перевод статьи John Au-Yeung: Why Do We Need Strict Mode in JavaScript?