ASI и мистические знаки перед IIFE в JavaScript
В JavaScript часто можно встретить такую констркуцию и различные вариации:
;(function($, undefined){
// my awesome code here
})(jqery);
В числе странных мистических знаков перед IIFE могут встречаться, наверное,
любые унарные операторы (~
, !
или void
) и точка с запятой. Впервые увидев в коде
что-то в духе ~(function(){}())
можно и испугаться, но на самом деле
причина прозаична.
ASI
В JavaScript есть механизм автоподстановки точек с запятой (ASI), позволяющий
программистам не заботиться о том, чтобы в конце выражения всегда стояла ;
—
ASI самостоятельно разберется с тем, чтобы в конце каждой строки стоял необходимый символ.
Но ASI срабатывает лишь в определенных случаях. Подробнее об этом можно
прочитать в спецификации, я же процитирую только самое интересное:
Когда в ходе последовательного разбора текста программы слева направо встречается токен (называемый токеном-нарушителем), который не позволяется никаким из правил грамматики, то точка с запятой автоматически вставляется перед токеном-нарушителем, если выполняется одно из следующих условий:
- Токен-нарушитель отделён от предыдущего токена как минимум одним КонцомСтроки.
- Токеном-нарушителем является закрывающая фигурная скобка }.
фото: RyanRWarner
Проще всего это объяснить на примере. И так, в первом случае ASI сработает, потому что
функции foo
не существует на уровне грамматики языка:
Но, могут быть случаи, когда сочетания символов между переводом строки допустимы грамматикой:
В этих случая ASI не сработает, и JavaScript проинтерпретирует эти строки несколько иначе, чем мы было ожидали:
Возвращаясь к точке с запятой перед вызовом функции, выходит, что строка, которая начинается
на (
, дефакто, находится в группе риска. Но, сложные системы не падают от одной ошибки.
Они падают от нескольких. И эти несколько ошибок порой сильно запутывают исследователей.
Сборка JavaScript
На большинстве сайтов JavaScript собирается в один файл с помощью какой-то вариации на тему cat *.js > app.js
,
затем еще минифицируется, и, скорее всего, регулярными выражениями, потому что строить AST-дерево
достаточно долго, и только затем уже записывается в известный каждому фронтент-разработчику файл app.min.js
.
Но вообще, все может не ограничиваться этим: разработчики могут, к примеру, решить, что нужно
вырезать console.log
на production-сборке, и, конечно, регулярными выражениями (Esprima, тебя не существует!).
Так вот, благодаря всему этому процессу, может легко получиться ситуация, в которой перед (function(){})()
появится что-то, что помешает ASI сделать свою работу, и наградит вас потрясающим гейзенбагом,
который будет пропадать, как только вы отключите процессинг JS. Конечно эта ошибка не такая
уж и сложная, но не имея представления о ASI, IIFE и минификации, можно серьезно запутаться
в происходящем.
По этому-то опытные разработчики ставят точку с запятой перед немедленно вызывающимися функциональными выражениями, предотвращая возможные баги во время сборки JavaScript.
Подписывайтесь на РСС. Всем добра и приключений!
фото: hperticarati
Похожие статьи:
-
Ссылколог-7
Курс по функциональному JS, CSS в GitHub, работа с памятью в JS, vimcasts и бесплатные книги по UX
-
Музыка для работы #5
Немного мурашек по спине…
-
Белый шум
Баттхёрта нить начинается здесь
-
CommonJS для браузера
Видео моего доклада на MoscowJS
-
Музыка для работы #4
Трогательный chillwave, dream pop & glich
-
Instapaper и Pocket
К чёртовой матери ссылки!
-
Byobu
Текстовый тайловый менеджер для Linux и OS X
-
Чейнинг
или Как сделать код проще, добавив одну строчку