Пользователь
0,0
рейтинг
4 мая 2011 в 13:14

Разработка → JavaScript Strict Mode перевод

В пятой редакции ECMAScript был представлен строгий режим (далее в статье Strict Mode). Strict Mode накладывает слой ограничений на JavaScript, он отгораживает вас от опасных частей языка (те части, которые есть исторически, но лучше чтобы их не было) и позволяет снизить вероятность ошибки.

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



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

Firefox 4 уже полностью поддерживает Strict Mode, а Chrome 11 практически полностью. Strict Mode уже не за горами — давайте изучим его подробнее!

Как включить Strict Mode?


Если добавить "use strict" в начало вашего JavaScript кода, то Strict Mode будет применен для всего кода:
"use strict";
012; //Восьмеричный литерал запрешен. Выбросит исключение SyntaxError в Strict Mode

В качестве альтернативы вы можете включить Strict Mode только в отдельной функции, добавив "use strict" в начало тела вашей функции:

012; //Нет ошибки (Strict Mode не включен глобально)
function foo() {
    "use strict";
    x=3; //Strict Mode выбросит исключение - запрещено неявно создавать глобальные переменные
}
foo(); //ReferenceError (Strict Mode включен для функции)


Наследуют ли внутренние функции Strict Mode от внешних функций?


Внутренняя функция, объявленная внутри внешней, в которой включен Strict Mode тоже будет иметь Strict Mode:

var wrapper = function(fn) {
  'use strict';
  var deleteNonConfigurable = function () {
    var obj = {};
    Object.defineProperty(obj, "name", {
      configurable: false
    });
    delete obj.name; // Выбросит исключение TypeError в Strict Mode
  }
  return deleteNonConfigurable;
}
 
wrapper()(); //TypeError (Strict Mode включен)

Важно запомнить, что Strict Mode не распространяется на «нестрогие» (ориг. non-strict) функции, которые выполняются внутри строгой функции (или они отправлены в функцию в качестве аргументов или выполняются, используя call или apply):

var test = function(fn) {
  'use strict';
  fn();
}
 
var deleteNonConfigurable = function () {
  var obj = {};
  Object.defineProperty(obj, "name", {
    configurable: false
  });
  delete obj.name; // Выбросит исключение TypeError в Strict Mode
}
 
test(deleteNonConfigurable); //нет ошибки (Strict Mode не применялся)

Почему я не могу включить Strict Mode в консоли моего браузера?


Когда выполняешь код в консоли фаербага или в других консолях использование "use strict" вне функции не имеет силы. Это потому, что большинство консолей обрамляют ваш код в eval'ом, поэтому ваш "use strict" не является первым выражением. Это можно обойти, обрамив ваш код в замыкание (IIFE), в начало которого мы положим "use strict" (но, когда я тестировал такой способ включения Strict Mode я понял, что это довольно неудобно, особенно если работать в консоли webkit developer tools — лучше тестировать ваш код на странице):

(function() {
    "use strict";
    var a;
    var b;
    function bar() {
        x = 5; //Strict Mode выбросит исключение за попытку создания глобальной переменной
    }
    bar(); // ReferenceError (Strict Mode включен)
})();

Что произойдет если мой браузер не поддерживает Strict Mode?


Ничего. Директива "use strict" это обычное строковое выражение, которое будет проигнорировано всеми движками JavaScript, которые не поддерживают Strict Mode. Это позволяет безопасно использовать синтаксис Strict Mode во всех браузерах без каких-либо опасений, в то время когда браузеры имеющие поддержку Strict Mode будут использовать его.

Какие правила включены в Strict Mode?


Правила определены в спецификации Strict Mode и включают в себя ограничения во время «компиляции» и интерпретации (выполнения скрипта). Это вводный обзор (каждое правило я описал с примерами в следующем параграфе): ecma262-5.com/ELS5_HTML.htm#Annex_C

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

В большинстве случаев Strict Mode предотвращает выполнение подозрительного или нелегального кода в процессе загрузки. Восьмеричные числа, дубли имен переменных, некорректное использование delete и попытки сделать что-нибудь этакие с eval и ключевым словом arguments, использование with приведет к исключению SyntaxError.

Слово this

В Strict Mode объект this не будет корректироваться. Это возможно самая интересная часть Strict Mode и самая тяжелая(шокирующая) для разработчиков. Все знают, что если первый аргумент call или apply — null или undefined, то значение this выполняемой функции будет преобразование в глобальный объект (для браузеров это window).

Прямое создание глобальных переменных

Не все согласятся с этим, но непрямое создание глобального объекта почти всегда является ошибкой. В Strict Mode вам выдадут красную карточку — ReferenceError.

arguments.caller и arguments.callee

Эти «полезные свойства» (от пер. никогда не применял их) запрещены в Strict Mode. Если вы используете их в вашем кода, то Strict Mode выбросит исключение.

Объявление существующего имени объекта

Когда вы создаете объект с двумя одинаковыми ключами, то Strict Mode выбросит исключение TypeError.

Тесты


Вот исходник моих Strict Mode тестов. Каждый набор тестов снабжен комментарием, ссылающемся на часть спецификации ECMAScript, которую он тестирует. Эта версия может быть выполнена в «режиме консоли». Т.е. вы можете скопировать тест, вставить в консоль и выполнить без изменений. Этот же код, работающий в режиме «HTML» я использовал для создания тестовой страницы, которую я представил вам в начале статьи. Этот исходник с дополнительными объектами в моем github репозитории. Я уверен, что там есть пара ошибок — не стесняйтесь присылать ошибки!

От переводчика: тут в статье шел огромный кусок кода, закинул его на pastebin

Заключение


Запрещение обращение к некоторым возможностям языка для улучшения кода — вопрос спорный, давайте отложим эти споры. В защиту Strict Mode я хочу сказать, что это отличный компромисс между тотальными переменами (которые сломают обратную совместимость) и ничего не деланием (которое приведет к захламлению языка и научит разработчиков плохому).

Что ещё почитать


ECMA-262 5th Edition: The Strict Mode of ECMAScript
Asen Bozhilov: Strict tester
Таблица совместимости с ECMAScript 5, часть посвященная Strict mode. Это отличный источник, часть большой таблицы совместимости, разработанной Юрием Зайцевым (Juriy Zaytsev aka «kangax»)

От переводчика. Strict Mode поддерживают практически половина всех браузеров, кроме своих прекрасных ограничений и иммунитету к распространенным ошибкам Strict Mode дает и другие преимущества (статья mraleph). Скоро неиспользование Strict Mode станет плохим тоном (аналогично requestAnimationFrame vs setTimeout). Сейчас самое время начать эксперименты!
Перевод: Angus Croll
Mikhail Davydov @azproduction
карма
449,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

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

  • 0
    Opera 11.10, 4/38, какая печаль.
    • 0
      Тоже самое — Opera 11.50 ( 4 out of 38 tests passed )
      • +3
        а с чего бы быть результату другим?
        • 0
          А почему бы и нет? Chrome 10.0.648.205 выдал только 2 из 38 против 32 в 11 версии из статьи.
          • –1
            может быть дело в том что у вас мажорная версия отличается от приведенной в статье?
            • 0
              Кто-то запретил Опере вносить изменения в поддержку strict mode в минорных релизах?
              • 0
                1. 11.50 это пока не релиз, а альфа-версия.

                2. Текущий публичный билд 11.50 имеет версию движка Presto/2.8.131, то есть такую же, как и 11.10.
                • 0
                  Presto — HTML-движек. За JavaScript у них вроде Carakan отвечает, если я ничего не пропустил.
                  • +2
                    вы не совсем правы :)
                    Carakan — это ECMScript-движок, который является лишь частью веб-движка Presto.
                    так что если версии Presto совпадают, то, конечно, и отдельные части движка идентичны, в том числе и JS.
          • 0
            Chromium 12.0.704.0 (78153) 34 из 38
            • +1
              Chrome 12.0.742.12 ( 37 out of 38 tests passed )
    • +1
      Firefox 4.0 38/38
  • +3
    Хорошо, и чем заменить arguments.callee?
  • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      Не только, к примеру можно сделать так:
      document.addEventListener('event', function(){
          console.log('будет вызвано только раз');
          document.removeEventListener('event', arguments.callee);
      });
      
      • 0
        document.addEventListener('click', function documentClickOnce(){
            alert('будет вызвано только раз');
            document.removeEventListener('click', documentClickOnce, false);
        }, false);
        Не вариант?
        PS 3 аргумент у addEventListener, removeEventListener ;)
        • 0
          Вариант конечно, но мне кажется что ссылка на функцию все равно должна быть, будь она именованная или неименованная.
          Я вообще както с подозрением отношусь к надобности strict mode. Те кто действительно знают язык не попадают в неприятные ситуации.

          PS привычка из mootools'овского addEvent ;)
        • 0
          Вариант, но мне не нравится именовать функцию, которая нигде прямо не вызывается. Это утяжеляет код. Добавляется возможность ошибки: в одном месте переименовал функцию, а в другом нет.
          • 0
            Именно в таком виде встречается значительно реже, чем:
            $('#pewpew').click(clickPewPew).unbind('click', clickPewPew);
            так или иначе приходится применять «имя функции».
            • 0
              Это не будет работать. Точнее, оно сработает, но вы не увидите результата работы.
  • +1
    DankoUA gro, можно вызывать функцию по имени, ссылка на себя есть в Named Function Expression, в то же время глобальное окружение не засоряется (по стандарту). С безымянными функциями, конечно, такой трюк не пройдет.
    (function pewpew(){
        console.log('pewpew'); 
        window.setTimeout(pewpew, 5000);
    }());
  • 0
    arguments.caller и arguments.callee

    Эти «полезные свойства» (от пер. никогда не применял их) запрещены в Strict Mode.


    Я применял — для диагностики.
  • +5
    IE10 — JavaScript Strict Mode Support: Microsoft Internet Explorer 10.0 ( 38 out of 38 tests passed )
  • 0
    Safari 5.0.5 — 1/38
    грусть-печаль((
  • 0
    Для использования стрикт-моде достаточно полной поддержки хотя бы в одном браузере. То есть код, оттебаженный под стрикт-моде заведомо будет работать без стрикт-моде.

    Поэтому похрен, сколько там в опере или сафари. Если под фф работает — знает отработает везде.
    • 0
      Единственный способ выстрелить себе в ногу с неполной поддержкой strict-mode — писать код на исключениях, которые выбрасываются в strict-mode и не выбрасываются при его отсутствии. Но это надо, наверное, какую-нибудь очень специфичную задачу решать, или иметь особый склад ума типа «буратино».
    • 0
      developer.mozilla.org/en/JavaScript/Strict_mode

      «Finally, make sure to test your code in browsers that do and don't support strict mode. If you test only in browsers that don't support strict mode, you're very likely to have problems in browsers that do, and vice versa.»

      Почитайте статью по ссылке выше, там более подробное подробное описание отличий Strict mode от «обычного».
  • 0
    > Если добавить «use strict» в начало вашего JavaScript кода

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

    > Это позволяет безопасно использовать синтаксис Strict Mode во всех браузерах без каких-либо опасений, в то время когда браузеры имеющие поддержку Strict Mode будут использовать его.

    > В Strict Mode объект this не будет корректироваться.

    безопасность благополучно зафейлили. это конечно классно, что this не корректируется, вот только поздновато спохватились — сразу надо было делать так, а теперь уже поздно.

    > Восьмеричные числа

    ну чем не угодили? почему 16-ричные оставили?

    > дубли имен переменных

    что в этом плохого? почему тогда не запретили присваивания в условных выражениях?

    > некорректное использование delete

    это какое? о0

    > и попытки сделать что-нибудь этакие с eval и ключевым словом arguments,

    ага, и взамен ничего дельного не предлагается. именованные функции — это конечно хорошо, только не кроссбраузерно. снова зафейлили совместимость. вызывающую функцию не узнать. парсить (new Error).stack?

    > использование with приведет к исключению SyntaxError.

    какого лешего? :-E

    > Не все согласятся с этим, но непрямое создание глобального объекта почти всегда является ошибкой. В Strict Mode вам выдадут красную карточку — ReferenceError.

    лучше бы такие переменные становились локальными, как в пхп.
    • 0
      это какое? о0

      Видимо, delete varName
      • 0
        могли бы и разрешить их удалять.
    • 0
      > Восьмеричные числа ну чем не угодили? почему 16-ричные оставили?
      Потому, что потенциальный источник ошибки. Ведущий ноль превращает десятичное в восьмеричное.
      • 0
        если в них нет цифр 8 и 9 :)
        • 0
          Естественно, что еще больше «усугубляет».
    • 0
      костыли. лучше бы нормальные инструкции процессору ввели. как в той же яве
      JavaScript развивается в направлении с сохранением полной совместимости со старыми версиями. К тому же "use string" вполне нормальная инструкция интерпретатору (не /*# use strict */ же писать).
      некорректное использование delete
      Это если попытаться удалить объект с флагом configurable: false раньше он назывался DontDelete
      "use strict";
      var a = {};
      Object.defineProperty(a, 'qqq', {});
      delete a.qqq; // TypeError: Cannot delete property 'qqq' of #<Object>
      вызывающую функцию не узнать
      не использовать такой подход, мне интересно в каких случаях без arguments.caller ну никак вообще?
      • 0
        при включении стрикта меняются некоторые аспектыы поведения. так что полная совместимость — это фейк. есть только синтаксическая совместимость, то есть браузеры которые не поддерживают стрикт не ругнутся, но и исполнять будут подругому.

        не нормальная, ибо нерасширяемая, не конфигурируемая, не допускает использования в других местах

        и при чём тут стрикт? удаление неудаляемого и без стрикта должно приводить к ошибке.

        function( id ){
        ensureTypes({ id: PositiveInteger })
        }
        реализуй ensureTypes без caller
        • 0
          Стрикт меняет подход к написанию, но не меняет качество на выходе, если написать ensureTypes вот так:
          function( id, blabal){
              ensureTypes(id, 'PositiveInteger')(blabla: 'String');
          }
          то можно обойтись и без caller. А вот передавать значение переменной косвенно по имени ссылки в контексте — это изврат извратов, любой сжимальшик js сделает из вашего кода баг.
          • 0
            то ensureTypes не сможет указать имя параметра которому передано неверное значение. закапываем.

            угу, заодно давайте откажемся от неявной передачи this, window и прочих лексически замкнутых переменных.
  • 0
    это какое? о0

  • 0
    От себя добавлю, что использование strict mode позволяет быстрее находить ошибки пре редактировании Джаваскрипта, например, WebStorm сразу подсвечивает доступ к глобальным переменным как ошибку (http://blogs.jetbrains.com/webide/tag/strict-mode/)
    • 0
      а без стриктмода не подсвечивает? выбрось каку.
      • 0
        Подсвечивает, но как предупреждение, а поскольку предупреждений много обычно бывает, то проблема не так заметна
  • 0
    Chrome 13.0.782.112 ( 38 out of 38 tests passed )

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.