Javascript use strict что это
Перейти к содержимому

Javascript use strict что это

  • автор:

Зачем нужен Strict Mode в JavaScript?

Iuliia Averianova

Строгий режим — это важная часть современного JavaScript. Он позволяет использовать ограниченный синтаксис JavaScript.

Семантика строгого режима отличается от старого “неаккуратного режима” JavaScript с его слабым синтаксисом и “замалчиваемыми” ошибками в коде — такие ошибки игнорируются, и код может запускаться с неожиданными результатами.

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

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

Строгий режим применим как к отдельным функциям, так и к целому скрипту. Его нельзя применять только к операторам и другим блокам, заключенным в фигурные скобки. Чтобы скрипт использовал строгий режим, добавляем оператор "use strict" или 'use strict' в начало скрипта перед всеми остальными операторами.

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

Чтобы применить строгий режим к функциям, нужно добавить оператор "use strict" или 'use strict' внутрь функции перед всеми остальными операторами. Он применяется ко всему, что находится внутри, включая вложенные функции.

В модулях JavaScript, представленных в ES2015, строгий режим применяется автоматически, поэтому его не нужно включать операторами.

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

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

  • преобразование ранее допустимых ошибок в ошибки синтаксиса или ошибки выполнения;
  • изменения, упрощающие вычисление конкретных переменных;
  • изменения, упрощающие функцию eval и объект arguments ;
  • изменения, которые будут применены в будущей спецификации ES.

Преобразование допустимых ошибок в недопустимые

В нестрогом режиме на некоторые допустимые ошибки JS никак не реагировал. Строгий режим ограничивает использование ошибочного синтаксиса и не позволяет коду запускаться с ошибками.

Он затрудняет создание глобальных переменных, не позволяя объявлять переменные с помощью var , let или const . Например, код ниже выдаст ReferenceError :

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

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

Например, в строгом режиме мы не можем присваивать значения переменным только для чтения: arguments , NaN или eval . Любое присвоение значений защищенным от записи глобальным переменным, свойствам только для геттеров и свойствам нерасширяемых объектов в строгом режиме выдаст исключение.

Ниже несколько примеров неудачного синтаксиса:

Все примеры выше выдадут TypeError . undefined и Infinity — глобальные переменные, защищенные от записи, obj — защищенное от записи свойство.

Свойство foo obj2 — единственное свойство геттера, и поэтому не может быть задано. fixedObj был защищен от добавления новых свойств методом Object.preventExtensions .

Кроме того, при попытке удалить неудаляемые свойства появится исключение TypeError , например:

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

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

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

Один из примеров — это оператор with . При его использовании интерпретатор JavaScript не знает, на какую переменную или свойство вы ссылаетесь, поскольку переменная с тем же именем может быть внутри или снаружи оператора with .

JavaScript не будет знать, x внутри оператора with ссылается на переменную x или свойство obj , obj.x .

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

Следующая вещь, запрещенная в строгом режиме — это объявление переменных внутри оператора eval .

Например, без строгого режима eval('let x') объявит переменную x внутри кода. Это позволяет программистам прятать объявление переменных в строках, что может блокировать объявление той же переменной вне оператора eval .

Чтобы предотвратить это, строгий режим не позволяет объявлять переменные в аргументе строки, который мы передаем внутрь оператора eval .

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

Запрет неверного синтаксиса

Неверный синтаксис метода 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.

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

Зачем в JavaScript нужен строгий режим?

Строгий режим (strict mode) — это важная часть современного JavaScript. Именно этот режим позволяет разработчикам пользоваться более ограниченным, чем стандартный, синтаксисом.

Семантика строгого режима отличается от традиционного нестрогого режима, который иногда называют «грязным» (sloppy mode). В таком режиме синтаксические правила языка не так строги, а когда происходят некоторые ошибки, система никак не оповещает о них пользователя. То есть — ошибки могут быть проигнорированы, а код, в котором они допущены, сможет выполняться дальше. Это способно привести к неожиданным результатам выполнения кода.

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

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

Особенности применения строгого режима

Строгий режим можно применять к отдельным функциям или к целому скрипту. Его нельзя применить только к отдельным инструкциям или к блокам кода, заключённым в фигурные скобки. Для того чтобы использовать строгий режим на уровне целого скрипта, в самое начало файла, до любых других команд, нужно поместить конструкцию «use strict» или ‘use strict’ .

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

Это приведёт к тому, что код, который не предназначен для выполнения в строгом режиме, окажется в таком состоянии, когда система попытается выполнить его в строгом режиме. Возможно и обратное — код, написанный для строгого режима, попадёт в нестрогий режим. Поэтому лучше всего не смешивать «строгие» и «нестрогие» скрипты.

Как уже было сказано, строгий режим можно применять к отдельным функциям. Для того чтобы это сделать — конструкцию «use strict» или ‘use strict’ надо поместить в верхнюю часть тела функции, до любых других команд. Строгий режим при таком подходе применяется ко всему, что размещено в теле функции, включая вложенные функции.

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

Изменения, вводимые в работу JS-кода строгим режимом

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

▍Преобразование «тихих» ошибок в исключения

«Тихие» ошибки преобразуются в строгом режиме в исключения. В нестрогом режиме на такие ошибки система явным образом не реагирует. В строгом же режиме наличие таких ошибок приводит к неработоспособности кода.

Так, благодаря этому сложно совершить ошибку случайного объявления глобальной переменной, так как переменные и константы в строгом режиме нельзя объявлять без использования директив var , let или const . В результате создание переменных без этих директив приведёт к неработоспособности программы. Например, попытка выполнения следующего кода приведёт к выдаче исключения ReferenceError :

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

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

Так, например, в строгом режиме нельзя выполнять операции присваивания значений таким сущностям, предназначенным только для чтения, как arguments , NaN или eval .

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

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

Попытка выполнения подобных фрагментов кода в строгом режиме приведёт к выдаче исключения TypeError . Так, например, undefined и Infinity — это глобальные сущности, значения которых нельзя перезаписывать, а свойство foo объекта obj не поддерживает перезапись. Свойство foo объекта obj2 имеет лишь геттер. Объект fixedObj сделан нерасширяемым с помощью метода Object.preventExtensions .

К выдаче TypeError приведёт и попытка удаления неудаляемого свойства:

Строгий режим запрещает назначать объекту свойства с одинаковыми именами. Как результат — попытка выполнения следующего кода приведёт к возникновению синтаксической ошибки:

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

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

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

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

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

Один из примеров подобного запрета касается инструкции with . Если пользоваться данной инструкцией, то это мешает JS-интерпретатору узнать о том, к какой именно переменной или к какому именно свойству мы обращаемся, так как возможно такое, что сущность с одним и тем же именем имеется и снаружи, и внутри блока инструкции with .

Предположим, есть такой код:

Интерпретатор не сможет узнать о том, ссылается ли переменная x , находящаяся внутри блока with , на внешнюю переменную x , или на свойство obj.x объекта obj .

В результате неясно — где именно в памяти будет расположено значение x . Для того чтобы избавиться от подобных неоднозначностей, в строгом режиме использование инструкции with запрещено. Посмотрим, что случится, если попытаться выполнить в строгом режиме следующий код:

Результатом этой попытки будет синтаксическая ошибка.

Ещё в строгом режиме запрещено объявлять переменные в коде, переданном методу eval .

Например, в обычном режиме команда вида eval(‘let x’) приведёт к объявлению переменной x . Это позволяет программистам скрывать объявления переменных в строках, что может привести к перезаписи определений тех же переменных, находящихся за пределами eval .

Для того чтобы это предотвратить, в строгом режиме запрещено объявлять переменные в коде, передаваемом в виде строки методу eval .

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

▍Запрет некорректных синтаксических конструкций

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

Вот примеры некорректного использования eval и arguments :

В строгом режиме нельзя создавать псевдонимы для объекта arguments и устанавливать новые значения arguments через эти псевдонимы.

В обычном режиме, если первым параметром функции является a , то установка в коде функции значения a приводит и к изменению значения в arguments[0] . В строгом же режиме в arguments всегда будет содержаться тот список аргументов, с которыми была вызвана функция.

Предположим, имеется следующий код:

В консоль попадёт [2,1] . Это так из-за того, что запись значения 2 в a не приводит к записи значения 2 в arguments[0] .

▍Оптимизации производительности

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

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

В строгом режиме ключевое слово this не обязано всегда быть объектом. В обычных условиях, если this функции привязывается, с помощью call , apply или bind , к чему-то, что не является объектом, к значению примитивного типа вроде undefined , null , number или boolean , подобное значение должно быть объектом.

Если контекст this меняется на что-то, не являющееся объектом, его место занимает глобальный объект. Например — window . Это означает, что если вызвать функцию, установив её this в некое значение, не являющееся объектом, вместо этого значения в this попадёт ссылка на глобальный объект.

Все команды console.log выведут true , так как в строгом режиме значение this в функции не заменяется автоматически ссылкой на глобальный объект в том случае, если this устанавливается в значение, не являющееся объектом.

▍Изменения, имеющие отношение к безопасности

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

В объекте arguments хранятся аргументы, переданные функции при её вызове. Например, если у нас имеется функция fn , это значит, что через fn.caller можно обратиться к функции, вызвавшей данную функцию, а с помощью fn.arguments можно увидеть аргументы, переданные fn при вызове.

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

В предыдущем примере мы не можем, в строгом режиме, обратиться к secretFunction.caller и secretFunction.arguments . Дело в том, что эти свойства можно использовать для получения стека вызовов функции. Если попытаться запустить этот код — будет выдано исключение TypeError .

В строгом режиме для именования переменных или свойств объектов нельзя использовать идентификаторы, которые могут найти применение в будущих версиях JavaScript. Речь идёт, например, о следующих идентификаторах: implements , interface , let , package , private , protected , public , static и yield .

В ES2015 и в более поздних версиях стандарта эти идентификаторы стали зарезервированными словами. И их нельзя использовать для именования переменных или свойств в строгом режиме.

Итоги

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

У современных браузеров не должно возникать сложностей со строгим режимом JavaScript. В результате можно сказать, что этот режим стоит использовать ради предотвращения «тихих» ошибок и ради повышения безопасности приложений. «Тихие» ошибки преобразуются в исключения, препятствующие выполнению программ, а в плане повышения безопасности можно, например, отметить механизмы строгого режима, ограничивающие eval и предотвращающие доступ к стеку вызовов функций. Кроме того, использование строгого режима облегчает оптимизацию кода JS-движками и заставляет программиста осторожно обращаться с зарезервированными словами, которые могут найти применение в будущих версиях JavaScript.

Уважаемые читатели! Пользуетесь ли вы строгим режимом при написании JS-кода своих проектов?

Name already in use

content / files / en-us / web / javascript / reference / strict_mode / index.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

Note: Sometimes you’ll see the default, non-strict mode referred to as sloppy mode. This isn’t an official term, but be aware of it, just in case.

JavaScript’s strict mode is a way to opt in to a restricted variant of JavaScript, thereby implicitly opting-out of «sloppy mode». Strict mode isn’t just a subset: it intentionally has different semantics from normal code. Browsers not supporting strict mode will run strict mode code with different behavior from browsers that do, so don’t rely on strict mode without feature-testing for support for the relevant aspects of strict mode. Strict mode code and non-strict mode code can coexist, so scripts can opt into strict mode incrementally.

Strict mode makes several changes to normal JavaScript semantics:

  1. Eliminates some JavaScript silent errors by changing them to throw errors.
  2. Fixes mistakes that make it difficult for JavaScript engines to perform optimizations: strict mode code can sometimes be made to run faster than identical code that’s not strict mode.
  3. Prohibits some syntax likely to be defined in future versions of ECMAScript.

Invoking strict mode

Strict mode applies to entire scripts or to individual functions. It doesn’t apply to block statements enclosed in <> braces; attempting to apply it to such contexts does nothing. eval code, Function code, event handler attributes, strings passed to setTimeout() , and related functions are either function bodies or entire scripts, and invoking strict mode in them works as expected.

Strict mode for scripts

To invoke strict mode for an entire script, put the exact statement «use strict»; (or ‘use strict’; ) before any other statements.

Strict mode for functions

Likewise, to invoke strict mode for a function, put the exact statement «use strict»; (or ‘use strict’; ) in the function’s body before any other statements.

The «use strict» directive can only be applied to the body of functions with simple parameters. Using «use strict» in functions with rest, default, or destructured parameters is a syntax error.

Strict mode for modules

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.

Strict mode for classes

All parts of a class’s body are strict mode code, including both class declarations and class expressions.

Changes in strict mode

Strict mode changes both syntax and runtime behavior. Changes generally fall into these categories:

  • changes converting mistakes into errors (as syntax errors or at runtime)
  • changes simplifying how variable references are resolved
  • changes simplifying eval and arguments
  • changes making it easier to write «secure» JavaScript
  • changes anticipating future ECMAScript evolution.

Converting mistakes into errors

Strict mode changes some previously-accepted mistakes into errors. JavaScript was designed to be easy for novice developers, and sometimes it gives operations which should be errors non-error semantics. Sometimes this fixes the immediate problem, but sometimes this creates worse problems in the future. Strict mode treats these mistakes as errors so that they’re discovered and promptly fixed.

Assigning to undeclared variables

Strict mode makes it impossible to accidentally create global variables. In sloppy mode, mistyping a variable in an assignment creates a new property on the global object and continues to «work». Assignments which would accidentally create global variables throw an error in strict mode:

Failing to assign to object properties

Strict mode makes assignments which would otherwise silently fail to throw an exception. There are three ways to fail a property assignment:

  • assignment to a non-writable data property
  • assignment to a getter-only accessor property
  • assignment to a new property on a non-extensible object

For example, NaN is a non-writable global variable. In sloppy mode, assigning to NaN does nothing; the developer receives no failure feedback. In strict mode, assigning to NaN throws an exception.

Failing to delete object properties

Attempts to delete a non-configurable or otherwise undeletable (e.g. it’s intercepted by a proxy’s deleteProperty handler which returns false ) property throw in strict mode (where before the attempt would have no effect):

Strict mode also forbids deleting plain names. delete name in strict mode is a syntax error:

If the name is a configurable global property, prefix it with globalThis to delete it.

Duplicate parameter names

Strict mode requires that function parameter names be unique. In sloppy mode, the last duplicated argument hides previous identically-named arguments. Those previous arguments remain available through arguments , so they’re not completely inaccessible. Still, this hiding makes little sense and is probably undesirable (it might hide a typo, for example), so in strict mode, duplicate argument names are a syntax error:

Legacy octal literals

Strict mode forbids a 0 -prefixed octal literal or octal escape sequence. In sloppy mode, a number beginning with a 0 , such as 0644 , is interpreted as an octal number ( 0644 === 420 ), if all digits are smaller than 8. Novice developers sometimes believe a leading-zero prefix has no semantic meaning, so they might use it as an alignment device — but this changes the number’s meaning! A leading-zero syntax for the octal is rarely useful and can be mistakenly used, so strict mode makes it a syntax error:

The standardized way to denote octal literals is via the 0o prefix. For example:

Octal escape sequences, such as «\45» , which is equal to «%» , can be used to represent characters by extended-ASCII character code numbers in octal. In strict mode, this is a syntax error. More formally, it’s disallowed to have \ followed by any decimal digit other than 0 , or \0 followed by a decimal digit; for example \9 and \07 .

Setting properties on primitive values

Strict mode forbids setting properties on primitive values. Accessing a property on a primitive implicitly creates a wrapper object that’s unobservable, so in sloppy mode, setting properties is ignored (no-op). In strict mode, a <> is thrown.

Duplicate property names

Duplicate property names used to be considered a <> in strict mode. With the introduction of computed property names, making duplication possible at runtime, this restriction was removed in ES2015.

Note: Making code that used to error become non-errors is always considered backwards-compatible. This is a good part of the language being strict about throwing errors: it leaves room for future semantic changes.

Simplifying scope management

Strict mode simplifies how variable names map to particular variable definitions in the code. Many compiler optimizations rely on the ability to say that variable X is stored in that location: this is critical to fully optimizing JavaScript code. JavaScript sometimes makes this basic mapping of name to variable definition in the code impossible to perform until runtime. Strict mode removes most cases where this happens, so the compiler can better optimize strict mode code.

Removal of the with statement

Strict mode prohibits with . The problem with with is that any name inside the block might map either to a property of the object passed to it, or to a variable in surrounding (or even global) scope, at runtime; it’s impossible to know which beforehand. Strict mode makes with a syntax error, so there’s no chance for a name in a with to refer to an unknown location at runtime:

The simple alternative of assigning the object to a short name variable, then accessing the corresponding property on that variable, stands ready to replace with .

In strict mode, eval does not introduce new variables into the surrounding scope. In sloppy mode, eval(«var x;») introduces a variable x into the surrounding function or the global scope. This means that, in general, in a function containing a call to eval , every name not referring to an argument or local variable must be mapped to a particular definition at runtime (because that eval might have introduced a new variable that would hide the outer variable). In strict mode, eval creates variables only for the code being evaluated, so eval can’t affect whether a name refers to an outer variable or some local variable:

Whether the string passed to eval() is evaluated in strict mode depends on how eval() is invoked (direct eval or indirect eval).

Block-scoped function declarations

The JavaScript language specification, since its start, had not allowed function declarations nested in block statements. However, it was so intuitive that most browsers implemented it as an extension grammar. Unfortunately, the implementations’ semantics diverged, and it became impossible for the language specification to reconcile all implementations. Therefore, block-scoped function declarations are only explicitly specified in strict mode (whereas they were once disallowed in strict mode), while sloppy mode behavior remains divergent among browsers.

Making eval and arguments simpler

Strict mode makes arguments and eval less bizarrely magical. Both involve a considerable amount of magical behavior in sloppy mode: eval to add or remove bindings and to change binding values, and arguments syncing named arguments with its indexed properties. Strict mode makes great strides toward treating eval and arguments as keywords.

Preventing binding or assigning eval and arguments

The names eval and arguments can’t be bound or assigned in language syntax. All these attempts to do so are syntax errors:

No syncing between parameters and arguments indices

Strict mode code doesn’t sync indices of the arguments object with each parameter binding. In a sloppy mode function whose first argument is arg , setting arg also sets arguments[0] , and vice versa (unless no arguments were provided or arguments[0] is deleted). arguments objects for strict mode functions store the original arguments when the function was invoked. arguments[i] does not track the value of the corresponding named argument, nor does a named argument track the value in the corresponding arguments[i] .

Strict mode makes it easier to write «secure» JavaScript. Some websites now provide ways for users to write JavaScript which will be run by the website on behalf of other users. JavaScript in browsers can access the user’s private information, so such JavaScript must be partially transformed before it is run, to censor access to forbidden functionality. JavaScript’s flexibility makes it effectively impossible to do this without many runtime checks. Certain language functions are so pervasive that performing runtime checks has a considerable performance cost. A few strict mode tweaks, plus requiring that user-submitted JavaScript be strict mode code and that it be invoked in a certain manner, substantially reduce the need for those runtime checks.

No this substitution

The value passed as this to a function in strict mode is not forced into being an object (a.k.a. «boxed»). For a sloppy mode function, this is always an object: either the provided object, if called with an object-valued this ; or the boxed value of this , if called with a primitive as this ; or the global object, if called with undefined or null as this . (Use call , apply , or bind to specify a particular this .) Not only is automatic boxing a performance cost, but exposing the global object in browsers is a security hazard because the global object provides access to functionality that «secure» JavaScript environments must restrict. Thus for a strict mode function, the specified this is not boxed into an object, and if unspecified, this is undefined instead of globalThis :

Removal of stack-walking properties

In strict mode it’s no longer possible to «walk» the JavaScript stack. Many implementations used to implement some extension features that make it possible to detect the upstream caller of a function. When a function fun is in the middle of being called, fun.caller is the function that most recently called fun , and fun.arguments is the arguments for that invocation of fun . Both extensions are problematic for «secure» JavaScript because they allow «secured» code to access «privileged» functions and their (potentially unsecured) arguments. If fun is in strict mode, both fun.caller and fun.arguments are non-deletable properties which throw when set or retrieved:

Similarly, arguments.callee is no longer supported. In sloppy mode, arguments.callee refers to the enclosing function. This use case is weak: name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed. arguments.callee for strict mode functions is a non-deletable property which throws an error when set or retrieved:

Extra reserved words

Reserved words are identifiers that can’t be used as variable names. Strict mode reserves some more names than sloppy mode, some of which are already used in the language, and some of which are reserved for the future to make future syntax extensions easier to implement.

  • implements
  • interface
  • package
  • private
  • protected
  • public

Transitioning to strict mode

Strict mode has been designed so that the transition to it can be made gradually. It is possible to change each file individually and even to transition code to strict mode down to the function granularity.

You can migrate a codebase to strict mode by first adding «use strict» to a piece of source code, and then fixing all execution errors, while watching out for semantic differences.

When adding ‘use strict’; , the following cases will throw a <> before the script is executing:

  • Octal syntax const n = 023; statement
  • Using delete on a variable name delete myVariable ;
  • Using eval or arguments as variable or function argument name
  • Using one of the newly reserved keywords (in prevision for future language features): implements , interface , let , package , private , protected , public , static , and yield
  • Declaring two function parameters with the same name function f(a, b, b) <>
  • Declaring the same property name twice in an object literal . This constraint was later removed (bug 1041128).

These errors are good, because they reveal plain errors or bad practices. They occur before the code is running, so they are easily discoverable as long as the code gets parsed by the runtime.

New runtime errors

JavaScript used to silently fail in contexts where what was done should be an error. Strict mode throws in such cases. If your code base contains such cases, testing will be necessary to be sure nothing is broken. You can screen for such errors at the function granularity level.

  • Assigning to an undeclared variable throws a <>. This used to set a property on the global object, which is rarely the expected effect. If you really want to set a value to the global object, explicitly assign it as a property on globalThis .
  • Failing to assign to an object’s property (e.g. it’s read-only) throws a <>. In sloppy mode, this would silently fail.
  • Deleting a non-deletable property throws a <>. In sloppy mode, this would silently fail.
  • Accessing arguments.callee , strictFunction.caller , or strictFunction.arguments throws a <> if the function is in strict mode. If you are using arguments.callee to call the function recursively, you can use a named function expression instead.

These differences are very subtle differences. It’s possible that a test suite doesn’t catch this kind of subtle difference. Careful review of your code base will probably be necessary to be sure these differences don’t affect the semantics of your code. Fortunately, this careful review can be done gradually down the function granularity.

Strict mode

Режим strict (строгий режим), введённый в ECMAScript 5, позволяет использовать более строгий вариант JavaScript. Это не просто подмножество языка: в нем сознательно используется семантика, отличающаяся от обычно принятой. Не поддерживающие строгий режим браузеры будут по-другому выполнять код, написанный для строгого режима, поэтому не полагайтесь на строгий режим без тестирования поддержки используемых особенностей этого режима. Строгий и обычный режим могут сосуществовать одновременно, а скрипт может переключаться в строгий режим по мере надобности.

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

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

Активизация строгого режима

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

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

Чтобы активизировать строгий режим для всего скрипта, нужно поместить оператор «use strict»; или ‘use strict’; перед всеми остальными операторами скрипта (выдержать приведённый синтаксис буквально).

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

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

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

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

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

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

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

Строгий режим изменяет синтаксис и поведение среды исполнения. Изменения главным образом попадают в следующие категории: преобразование ошибок в исключения; изменения, упрощающие вычисление переменной в определённых случаях использования её имени; изменения, упрощающие eval и arguments ; изменения, упрощающие написание «безопасного» JavaScript, и изменения, предвосхищающие дальнейшее развитие ECMAScript.

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

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

Во-первых, строгий режим делает невозможным случайное создание глобальных переменных. В обычном JavaScript опечатка в имени переменной во время присваивания приводит к созданию нового свойства глобального объекта, и выполнение продолжается (хотя в современном JavaScript оно, вероятно, аварийно завершится в дальнейшем). Присваивания, которые могут случайно создать глобальную переменную, в строгом режиме выбрасывают исключение:

Во-вторых, строгий режим заставляет присваивания, которые всё равно завершились бы неудачей, выбрасывать исключения. Например, NaN — глобальная переменная, защищённая от записи. В обычном режиме присваивание NaN значения ничего не делает; разработчик не получает никакого сообщения об ошибке. В строгом режиме присваивание NaN значения выбрасывает исключение. Любое присваивание, которое в обычном режиме завершается неудачей (присваивание значения свойству, защищённому от записи; присваивание значения свойству, доступному только на чтение; присваивание нового свойства нерасширяемому объекту), в строгом режиме выбросит исключение:

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

В-четвёртых, строгий режим требует, чтобы все свойства, перечисленные в сериализованном объекте, встречались только один раз. В обычном коде имена свойств могут дублироваться, а значение свойства определяется последним объявлением. Но, в таком случае, дублирование — просто почва для багов, если код редактируется с тем, чтобы поменять значение свойства как-то по-другому, кроме изменения последнего объявления. Дублирование имён свойств в строгом режиме является синтаксической ошибкой:

Примечание: Это уже не является проблемой в ECMAScript 2015 (баг 1041128).

В-пятых, строгий режим требует, чтобы имена аргументов в объявлении функций встречались только один раз. В обычном коде последний повторённый аргумент скрывает предыдущие аргументы с таким же именем. Эти предыдущие аргументы всё ещё доступны через arguments[i] , так что они не полностью потеряны. Тем не менее, такое сокрытие несёт в себе мало смысла и, скорее всего, не имеет под собой цели (например, может скрывать опечатку), поэтому в строгом режиме дублирование имён аргументов является синтаксической ошибкой:

В-шестых, строгий режим запрещает синтаксис восьмеричной системы счисления. Восьмеричный синтаксис не является частью ECMAScript, но поддерживается во всех браузерах с помощью дописывания нуля спереди к восьмеричному числу: 0644 === 420 и «\045» === «%» . В ECMAScript 2015 восьмеричное число поддерживается также с помощью дописывания перед числом » 0o «. Т.е.

Иногда начинающие разработчики думают, что ведущий ноль не имеет семантического значения, и используют его для выравнивания — но это меняет значение числа! Восьмеричный синтаксис редко бывает полезен и может быть неправильно использован, поэтому строгий режим считает восьмеричные числа синтаксической ошибкой:

В-седьмых, строгий режим в ECMAScript 2015 запрещает установку свойств primitive значениям. Без строгого режима, установка свойств просто игнорируется (no-op), со строгим режимом, однако, выдаёт TypeError .

Упрощение работы с переменными

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

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

Простая альтернатива with уже существует — присваивание объекта переменной с коротким именем и затем доступ к нужному свойству как свойству этой переменной.

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

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

Таким образом, имена в строгом коде, передаваемом в eval , ведут себя так же, как имена в нестрогом коде, передаваемом в eval внутри строгого режима.

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

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

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

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

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

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

«Безопасный» JavaScript

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

Во-первых, значение, передаваемое в функцию как this , в строгом режиме не приводится к объекту (не «упаковывается»). В обычной функции this всегда представляет собой объект: либо это непосредственно объект, в случае вызова с this , представляющим объект-значение; либо значение, упакованное в объект, в случае вызова с this типа Boolean, string, или number; либо глобальный объект, если тип this это undefined или null . (Для точного определения конкретного this используйте call , apply , или bind .) Автоматическая упаковка не только снижает производительность, но и выставляет на показ глобальный объект, что в браузерах является угрозой безопасности, потому что глобальный объект предоставляет доступ к функциональности, которая должна быть ограничена в среде «безопасного» JavaScript. Таким образом, для функции в строгом режиме точно определённый this не упаковывается в объект, а если не определён точно, this является undefined :

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

В-третьих, в функциях строгого режима свойство arguments больше не предоставляет доступ к переменным, созданным внутри функции. В некоторых предыдущих реализациях ECMAScript arguments.caller представлял собой объект, свойства которого являлись ссылками на переменные, созданные внутри функции при её вызове. Это представляет собой угрозу безопасности, так как нарушает возможность скрывать приватные данные внутри функций (замыканий). Также это делает невозможными большинство оптимизаций. Исходя из этих причин, ни один из современных браузеров не реализует этого поведения. Но всё же, ввиду своей исторической функциональности, arguments.caller для функций в строгом режиме всё ещё является неудаляемым свойством, которое вызывает исключение при попытке его чтения или записи:

Подготовка почвы для будущих версий ECMAScript

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

Во-первых, в строгом режиме зарезервирован для использования следующий список ключевых слов: implements , interface , let , package , private , protected , public , static и yield . В строгом режиме, следовательно, вы не можете задействовать эти слова для именования или обращения к переменным или аргументам.

Два замечания, специфичных для Mozilla: Первое, если ваш код создан на JavaScript 1.7 или выше (например, chrome code, или тег <script type=»»> заполнен правильно), и применён строгий режим, то let и yield имеют ту же функциональность, которая у них была изначально, когда они только появились. Однако в веб, в строгом коде загруженном через <script src=»»> или <script>. </script> , нельзя будет использовать let / yield в качестве идентификаторов. Второе, в то время как ES5 зарезервировал слова class , enum , export , extends , import и super для любого режима, в Firefox 5 Mozilla они были зарезервированы намного раньше и лишь для строгого режима.

Во-вторых, в строгом режиме запрещается объявление функций глубже самого верхнего уровня скрипта или функции. В обычном коде в браузерах, объявление функций позволено «везде», что не является частью ES5 (или даже ES3!) Это расширение различных браузеров, не имеющее общего совместимого подхода. Есть надежда, что в последующих редакциях ECMAScript будет определена новая семантика для объявления функций вне верхнего уровня скрипта или функции. Запрет на объявление таких функций в строгом режиме производит «зачистку» для спецификации в будущем релизе ECMAScript:

Данный запрет не является особенностью строгого режима, потому что такое объявление функций является одним из расширений основного ES5. Но это рекомендация комитета ECMAScript, и браузеры реализуют её.

Строгий режим в браузерах

В большинстве браузеров в настоящее время строгий режим реализован. Однако не стоит впадать в слепую зависимость от него, потому что существует множество Версий браузеров, поддерживающих строгий режим лишь частично или вовсе не поддерживающих оный (например, Internet Explorer ниже версии 10!). Строгий режим изменяет семантику. Надежда на эти изменения приведёт к ошибкам и погрешностям в браузерах, в которых строгий режим не реализован. Проявляйте осторожность при использовании строгого режима, и подкрепляйте надёжность строгого режима тестами особенностей, которые проверяют, насколько верно реализованы его фрагменты. Наконец, старайтесь тестировать свой код в браузерах, как поддерживающих, так и не поддерживающих строгий режим. Если вы проводите тестирование только в тех браузерах, которые не поддерживают строгий режим, то вполне вероятно у вас появятся проблемы в браузерах, его поддерживающих, и наоборот.

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

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