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

Строгий режим вносит в семантику JavaScript некоторые изменения. Он не даёт системе закрывать глаза на ошибки, выдавая соответствующие исключения. Это приводит к остановке выполнения программ.
Строгий режим, кроме того, помогает в написании программ, в которых нет недочётов, мешающих JS-движкам оптимизировать код. Далее, в этом режиме запрещено использование элементов синтаксиса, которые могут получить особый смысл в будущих версиях языка.
Особенности применения строгого режима
Строгий режим можно применять к отдельным функциям или к целому скрипту. Его нельзя применить только к отдельным инструкциям или к блокам кода, заключённым в фигурные скобки. Для того чтобы использовать строгий режим на уровне целого скрипта, в самое начало файла, до любых других команд, нужно поместить конструкцию
"use strict" или 'use strict'.Если в проекте имеются некоторые скрипты, в которых не используется строгий режим, и другие, в которых этот режим используется, тогда может случиться так, что эти скрипты окажутся объединены.
Это приведёт к тому, что код, который не предназначен для выполнения в строгом режиме, окажется в таком состоянии, когда система попытается выполнить его в строгом режиме. Возможно и обратное — код, написанный для строгого режима, попадёт в нестрогий режим. Поэтому лучше всего не смешивать «строгие» и «нестрогие» скрипты.
Как уже было сказано, строгий режим можно применять к отдельным функциям. Для того чтобы это сделать — конструкцию
"use strict" или 'use strict' надо поместить в верхнюю часть тела функции, до любых других команд. Строгий режим при таком подходе применяется ко всему, что размещено в теле функции, включая вложенные функции.Например:
const strictFunction = ()=>{
'use strict';
const nestedFunction = ()=>{
// эта функция тоже использует строгий режим
}
}В JavaScript-модулях, которые появились в стандарте ES2015, строгий режим включён по умолчанию. Поэтому при работе с ними включать его явным образом не нужно.
Изменения, вводимые в работу JS-кода строгим режимом
Строгий режим влияет и на синтаксис кода, и на то, как код ведёт себя во время выполнения программы. Ошибки в коде преобразуются в исключения. То, что в нестрогом режиме тихо даёт сбой, в строгом вызывает сообщение об ошибке. Это похоже на то, как в нестрогом режиме система реагирует на синтаксические ошибки. В строгом режиме упрощается работа с переменными, жёстко регулируется использование функции
eval и объекта arguments, упорядочивается работа с конструкциями, которые могут быть реализованы в будущих версиях языка.?Преобразование «тихих» ошибок в исключения
«Тихие» ошибки преобразуются в строгом режиме в исключения. В нестрогом режиме на такие ошибки система явным образом не реагирует. В строгом же режиме наличие таких ошибок приводит к неработоспособности кода.
Так, благодаря этому сложно совершить ошибку случайного объявления глобальной переменной, так как переменные и константы в строгом режиме нельзя объявлять без использования директив
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 — это глобальные сущности, значения которых нельзя перезаписывать, а свойство foo объекта obj не поддерживает перезапись. Свойство foo объекта obj2 имеет лишь геттер. Объект fixedObj сделан нерасширяемым с помощью метода Object.preventExtensions.К выдаче
TypeError приведёт и попытка удаления неудаляемого свойства:'use strict';
delete Array.prototypeСтрогий режим запрещает назначать объекту свойства с одинаковыми именами. Как результат — попытка выполнения следующего кода приведёт к возникновению синтаксической ошибки:
'use strict';
let o = { a: 1, a: 2 };Строгий режим требует, чтобы имена параметров функций были бы уникальными. В нестрогом режиме, если, например, два параметра функции имеют одно и то же имя
one, тогда, при передаче функции аргументов, значением параметра станет то, что попало в аргумент, объявленный последним.В строгом режиме запрещены параметры функций с одинаковыми именами. В результате попытка выполнения следующего кода приведёт к возникновению синтаксической ошибки:
'use strict';
const multiply = (x, x, y) => x*x*y;В строгом режиме нельзя использовать восьмеричную запись чисел, предваряя число нулём. Этого нет в спецификации, но данная возможность поддерживается браузерами.
Такое положение дел путает разработчиков, заставляя их полагать, что 0, предшествующий числу, просто игнорируется, не имея особого смысла. В строгом режиме попытка воспользоваться числом, в начале которого стоит 0, приведёт к синтаксической ошибке.
Строгий режим, кроме того, запрещает использование конструкций, затрудняющих оптимизацию. Интерпретатору, перед выполнением оптимизации кода, нужно знать о том, что переменная хранится именно там, где, как считает интерпретатор, она хранится. В строгом режиме запрещается то, что мешает оптимизациям.
Один из примеров подобного запрета касается инструкции
with. Если пользоваться данной инструкцией, то это мешает JS-интерпретатору узнать о том, к какой именно переменной или к какому именно свойству мы обращаемся, так как возможно такое, что сущность с одним и тем же именем имеется и снаружи, и внутри блока инструкции with.Предположим, есть такой код:
let x = 1;
with (obj) {
x;
}Интерпретатор не сможет узнать о том, ссылается ли переменная
x, находящаяся внутри блока with, на внешнюю переменную x, или на свойство obj.x объекта obj.В результате неясно — где именно в памяти будет расположено значение
x. Для того чтобы избавиться от подобных неоднозначностей, в строгом режиме использование инструкции with запрещено. Посмотрим, что случится, если попытаться выполнить в строгом режиме следующий код:'use strict';
let x = 1;
with (obj) {
x;
}Результатом этой попытки будет синтаксическая ошибка.
Ещё в строгом режиме запрещено объявлять переменные в коде, переданном методу
eval.Например, в обычном режиме команда вида
eval('let x') приведёт к объявлению переменной x. Это позволяет программистам скрывать объявления переменных в строках, что может привести к перезаписи определений тех же переменных, находящихся за пределами eval.Для того чтобы это предотвратить, в строгом режиме запрещено объявлять переменные в коде, передаваемом в виде строки методу
eval.Строгий режим, кроме того, запрещает удаление обычных переменных. В результате попытка выполнить следующий код приведёт к синтаксической ошибке:
'use strict';
let x;
delete x;?Запрет некорректных синтаксических конструкций
В строгом режиме запрещено неправильное использование
eval и arguments. Речь идёт о запрете всяческих манипуляций с ними. Например — это нечто вроде присваивания им новых значений, использование их имён в роли имён переменных, функций, параметров функций.Вот примеры некорректного использования
eval и arguments:'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 и устанавливать новые значения 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 не приводит к записи значения 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 попадёт ссылка на глобальный объект.Рассмотрим пример:
'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);Все команды
console.log выведут true, так как в строгом режиме значение this в функции не заменяется автоматически ссылкой на глобальный объект в том случае, если this устанавливается в значение, не являющееся объектом.?Изменения, имеющие отношение к безопасности
В строгом режиме нельзя делать общедоступными свойства функции
caller и arguments. Дело в том, что caller, например, может дать доступ к функции, вызвавшей функцию, к свойству caller которой мы обращаемся.В объекте
arguments хранятся аргументы, переданные функции при её вызове. Например, если у нас имеется функция fn, это значит, что через fn.caller можно обратиться к функции, вызвавшей данную функцию, а с помощью fn.arguments можно увидеть аргументы, переданные fn при вызове.Эти возможности представляют собой потенциальную угрозу безопасности. В результате в строгом режиме доступ к этим свойствам запрещён.
function secretFunction() {
'use strict';
secretFunction.caller;
secretFunction.arguments;
}
function restrictedRunner() {
return secretFunction();
}
restrictedRunner();В предыдущем примере мы не можем, в строгом режиме, обратиться к
secretFunction.caller и secretFunction.arguments. Дело в том, что эти свойства можно использовать для получения стека вызовов функции. Если попытаться запустить этот код — будет выдано исключение TypeError.В строгом режиме для именования переменных или свойств объектов нельзя использовать идентификаторы, которые могут найти применение в будущих версиях JavaScript. Речь идёт, например, о следующих идентификаторах:
implements, interface, let, package, private, protected, public, static и yield.В ES2015 и в более поздних версиях стандарта эти идентификаторы стали зарезервированными словами. И их нельзя использовать для именования переменных или свойств в строгом режиме.
Итоги
Строгий режим — это стандарт, который существует уже многие годы. Он пользуется чрезвычайно широкой поддержкой браузеров. Проблемы с кодом, выполняемом в строгом режиме, могут возникать лишь у старых браузеров, таких, как Internet Explorer.
У современных браузеров не должно возникать сложностей со строгим режимом JavaScript. В результате можно сказать, что этот режим стоит использовать ради предотвращения «тихих» ошибок и ради повышения безопасности приложений. «Тихие» ошибки преобразуются в исключения, препятствующие выполнению программ, а в плане повышения безопасности можно, например, отметить механизмы строгого режима, ограничивающие
eval и предотвращающие доступ к стеку вызовов функций. Кроме того, использование строгого режима облегчает оптимизацию кода JS-движками и заставляет программиста осторожно обращаться с зарезервированными словами, которые могут найти применение в будущих версиях JavaScript.Уважаемые читатели! Пользуетесь ли вы строгим режимом при написании JS-кода своих проектов?

Комментарии (13)

Klestofer
25.11.2019 16:07Строгий режим запрещает назначать объекту свойства с одинаковыми именами
Начиная с ECMAScript 2015, синтаксическая ошибка в коде
не возникает. В MDN об этом упоминается. Только непонятно почему так сделали. Кто-нибудь знает?'use strict'; var o = { a: 1, a: 2 };
faiwer
25.11.2019 16:26Рискну предположить что для такого случая:
const o = { ...a, a: 2 }; // where a = { a: 1 }
чтобы в этом случае не выкидывало ошибку.

Tarik02
25.11.2019 20:31Нет, это же не из той оперы. А вообще, spread оператор обычно транспилируется (и рискну предположить, что роботает) через Object.assign, который уже не имеет никакого отношения к синтаксису.

faiwer
25.11.2019 22:18+1Отчего же? Консистентность языковых конструкций достаточно важна. Если используя spread можно задать два одинаковых ключа, а без него нельзя — то это нехорошо. Хотя версия с JSON вероятнее :-)

Tarik02
26.11.2019 09:11С одной стороны да, но с другой, было бы очень странно если бы spread оператор давал ошибку когда есть два одинаковых ключа даже если бы в литерале объекта это было бы ошибкой.

znsoft
26.11.2019 03:22+1а можно ли включить для функции или скрипта «sloppy mode»? Например: в хроме для одноразовой задачки (нахождения оптимального решения) прямо в консоли разработчика понадобилось использовать eval внутри setTimeout и… хром не разрешает так делать
Xalium
Только если эта переменная не объявлена в "родительских" областях видимости.