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

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

Цель этой статьи: предоставить для разработчиков руководство по переходу к строгому режиму.

Постепенный переход

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

Различия non-strict и strict режимов

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

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

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

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

Новые ошибки во время выполнения(runtime errors)

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

Установка значения необъявленной переменной

function f(x){
  "use strict";
  var a = 12;
  b = a + x*35; // error!
}
f();

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

var global = this; // в верхнем контексте "this" всегда
                   // ссылается на глобальный объект
function f(){
  "use strict";
  var a = 12;
  global.b = a + x*35;
}
f();

Попытка удалить неконфигурируемое свойство

"use strict";
delete Object.prototype; // error!

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

Отравленные аргументы (arguments) и свойства функции

Обращение к arguments.callee, arguments.caller, anyFunction.caller, или anyFunction.arguments выбросит ошибку в строгом режиме. Единственный законный способ повторного использования функции как в:

// Пример взят с vanillajs: http://vanilla-js.com/
var s = document.getElementById('thing').style;
s.opacity = 1;
(function(){ 
  if((s.opacity-=.1) < 0)
    s.display="none";
  else
    setTimeout(arguments.callee, 40);
})();

может быть переписан как:

"use strict";
var s = document.getElementById('thing').style;
s.opacity = 1;
(function fadeOut(){ // имя функции
  if((s.opacity-=.1) < 0)
    s.display="none";
  else
    setTimeout(fadeOut, 40); // используется имя функции
})();

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

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

this в вызовах фукнции

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

arguments не являтся псевдонимом аргументов именнованной функции

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

Изменения в eval

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

Строго нейтральный код

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

  1. Пишите ваш код в строгом режиме и убедитесь в отсутствии ошибок только строго режима (из секции выше "Новые ошибки во время выполнения").
  2. Держитесь подальше от семантических различий
    1. eval: использовать только тогда, когда вы знаете что делаете
    2. arguments: всегда обращайтесь в фукнции к объекту arguments через его имя или сделайте его копию :
      var args = Array.prototype.slice.call(arguments)
      в первой строчке вашей функции
    3. this: использовать this только тогда, когда он ссылается на объект, созданный вами

Смотрите также

Метки документа и участники

Метки: 
 Внесли вклад в эту страницу: ifeature, akmil, tselishev-semen
 Обновлялась последний раз: ifeature,