Строгий режим — это важная часть современного 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. Любое присвоение значений защищенным от записи глобальным переменным, свойствам только для геттеров и свойствам нерасширяемых объектов в строгом режиме выдаст исключение.
Ниже несколько примеров неудачного синтаксиса:
'use strict'; let undefined = 5; let Infinity = 5; let obj = {}; Object.defineProperty(obj, 'foo', { value: 1, writable: false }); obj.foo = 1 let obj2 = { get foo() { return 17; } }; obj2.foo = 2 let fixedObj = {}; Object.preventExtensions(fixedObj); fixed.bar= 1;
Все примеры выше выдадут 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:
'use strict'; eval = 1; arguments++; arguments--; ++eval; eval--; let obj = { set p(arguments) { } }; let eval; try { } catch (arguments) { } try { } catch (eval) { } function x(eval) { } function arguments() { } let y = function eval() { }; let eval = ()=>{ }; let f = new Function('arguments', "'use strict'; return 1;");
Строгий режим не разрешает создавать псевдоним для объекта arguments и задавать с ним новые значения.
Без строгого режима, если первый параметр функции — a, тогда установка a также задает arguments[0]. В строгом режиме у объекта arguments всегда будет список аргументов, с которыми вызывается функция.
Например, если у нас есть:
const fn = function(a) { 'use strict'; a = 2; return [a, arguments[0]]; }console.log(fn(1))
Тогда мы должны увидеть [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, связанным с необъектным типом.
Например, если мы запустим код ниже:
'use strict';function fn() { return this; } console.log(fn() === undefined); console.log(fn.call(2) === 2); console.log(fn.apply(null) === null); console.log(fn.call(undefined) === undefined); console.log(fn.bind(true)() === true);
Все журналы консоли будут иметь значение true, так как this внутри функции не преобразуется автоматически в глобальный объект window, когда this меняется на что-то, имеющее необъектный тип.
Исправления безопасности
В строгом режиме caller и arguments не публичные, так как caller может отображать функцию, вызывающую другую функцию, к которой обращается это свойство caller.
В arguments есть аргументы, передаваемые при вызове функции. Например, если у нас есть функция fn, тогда через fn.caller мы можем увидеть аргументы, которые были переданы в fn, когда был совершен ее вызов.
Это создает потенциальную дыру в безопасности, которая устраняется запретом доступа к этим двум свойствам функции.
function secretFunction() { 'use strict'; secretFunction.caller; secretFunction.arguments; } function restrictedRunner() { return secretFunction(); } restrictedRunner();
В строгом режиме в примере выше мы не сможем получить доступ к secretFunction.caller и secretFunction.arguments, так как люди могут использовать их для получения стека функций. При выполнении код выдаст TypeError.
Идентификаторы, которые станут ключевыми словами в будущих версиях JavaScript, нельзя использовать для именования переменных или свойств объектов. В ES2015 или позже следующие ключевые слова стали зарезервированными, и их нельзя использовать для определения идентификаторов в коде: implements, interface, let, package, private, protected, public, static и yield.
Строгий режим был стандартом несколько лет. Обычно браузеры его поддерживают. Проблемы могут возникнуть только в старых браузерах, таких как Internet Explorer.
У других браузеров не должно быть проблем в работе со строгим режимом, поэтому его стоит использовать для предотвращения перечисленных выше ошибок.
Специально для сайта ITWORLD.UZ. Новость взята с сайта NOP::Nuances of programming