Что такое use strict
Переход к строгому режиму
Цель этой статьи: предоставить для разработчиков руководство по переходу к строгому режиму.
Постепенный переход
Строгий режим был спроектирован таким образом, чтобы переход к нему можно было сделать постепенно. Каждый файл можно переводить к строгому режиму поодиночке, и даже есть возможность включить строгий режим для каждой функции по отдельности.
Различия non-strict и strict режимов
Синтаксические ошибки
При добавлении «use strict»; следующие случаи вызывают SyntaxError до выполнения скрипта:
Эти ошибки хороши тем, что обличают скользкие, едва уловимые ошибки и плохие практики написания кода.
Новые ошибки времени выполнения (runtime errors)
Ранее JavaScript не показывал никаких ошибок и предупреждений в некоторых случаях выполнения некорректного кода. Строгий режим выбрасывает исключения в таких случаях. Если в вашем коде есть такие случаи, тестирование будет необходимо, чтобы убедиться, что ничего не сломалось после перехода к строгому режиму. Ещё раз это может случится на уровне детализации функции.
Установка значения необъявленной переменной
Здесь изменяется значение глобального объекта, что редко является ожидаемым эффектом. Если вы действительно хотите изменить значение глобального объекта, передайте его в качестве аргумента функции и явно присвойте его как свойство:
Попытка удалить неконфигурируемое свойство
В нестрогом режиме этот код может молчаливо выполниться неудачей и ничего не сделать, вопреки ожиданиям.
Отравленные аргументы (arguments) и свойства функции
может быть переписан как:
Семантические различия
Эти различия очень тонкие. Вполне возможно, что тесты не поймают этот тип едва уловимых отличий. Вероятно, потребуется тщательная рецензия кода, чтобы удостовериться, что эти различия не влияют на семантику вашего кода. К счастью, этот анализ может быть сделан постепенно, спускаясь вниз к реализации каждой конкретной функции.
this в вызовах функции
arguments не является псевдонимом именованных аргументов функции
В нестрогом режиме изменение значения в объекте arguments изменяло соответствующий именованный аргумент функции. Это усложняло оптимизацию кода для движков JavaScript и сам код становился менее читабельным и понятным. В строгом режиме объект arguments создаётся и инициализируется с теми же значениями, что и именованные аргументы, но изменения объекта arguments или именованных аргументов теперь никак не влияют друг на друга.
Изменения в eval
В строгом режиме eval не создаёт новой переменной в той области видимости, где был вызван. Также, конечно, в строгом режиме, строка выполняется с правилами строгого режима. Потребуется провести тщательное тестирование, чтобы убедиться, что ничего не сломалось. Не использовать eval, если он вам действительно не нужен, может быть другим прагматичным решением.
Строго-нейтральный код
Strict mode
Режим strict (строгий режим), введённый в ECMAScript 5, позволяет использовать более строгий вариант JavaScript. Это не просто подмножество языка: в нем сознательно используется семантика, отличающаяся от обычно принятой. Не поддерживающие строгий режим браузеры будут по-другому выполнять код, написанный для строгого режима, поэтому не полагайтесь на строгий режим без тестирования поддержки используемых особенностей этого режима. Строгий и обычный режим могут сосуществовать одновременно, а скрипт может переключаться в строгий режим по мере надобности.
Активизация строгого режима
Строгий режим для скриптов
Чтобы активизировать строгий режим для всего скрипта, нужно поместить оператор «use strict»; или ‘use strict’; перед всеми остальными операторами скрипта (выдержать приведённый синтаксис буквально).
В этой синтаксической конструкции кроется ловушка, в которую уже угодили даже самые известные сайты: нельзя бездумно объединять скрипты с разными режимами. Объединение скрипта в строгом режиме со скриптом в обычном выглядит как скрипт в строгом режиме! Справедливо и обратное: объединение обычного скрипта со строгим выглядит как нестрогий скрипт. Объединение только строгих или только обычных скриптов проходит без последствий, проблему вызывает совместное использование скриптов со строгим и обычным режимом. Поэтому рекомендуется включать строгий режим только на уровне функций (хотя бы в течение переходного периода).
Вы также можете использовать подход «обёртывания» всего содержимого скрипта в функцию, для которой включён строгий режим. Это уменьшит возможность возникновения проблем при объединении скриптов, но одновременно потребует явно экспортировать из контекста функции все глобальные переменные.
Строгий режим для функций
Аналогично, чтобы включить строгий режим для функции, поместите оператор «use strict»; (или ‘use strict’; ) в тело функции перед любыми другими операторами.
Строгий режим для модулей
ECMAScript 2015 представил модули JavaScript и, следовательно, 3-й способ войти в строгий режим. Все содержимое модулей JavaScript автоматически находится в строгом режиме, и для его запуска не требуется никаких инструкций.
Изменения в строгом режиме
Строгий режим изменяет синтаксис и поведение среды исполнения. Изменения главным образом попадают в следующие категории: преобразование ошибок в исключения; изменения, упрощающие вычисление переменной в определённых случаях использования её имени; изменения, упрощающие eval и arguments ; изменения, упрощающие написание «безопасного» JavaScript, и изменения, предвосхищающие дальнейшее развитие ECMAScript.
Преобразование ошибок в исключения
Строгий режим превращает некоторые прощавшиеся ранее ошибки в исключения. JavaScript был разработан с расчётом на низкий порог вхождения, и временами он придаёт заведомо ошибочным операциям семантику нормального кода. Иногда это помогает срочно решить проблему, а иногда это создаёт худшие проблемы в будущем. Строгий режим расценивает такие ошибки как ошибки времени выполнения, для того чтобы они могли быть обнаружены и исправлены в обязательном порядке.
Во-первых, строгий режим делает невозможным случайное создание глобальных переменных. В обычном JavaScript опечатка в имени переменной во время присваивания приводит к созданию нового свойства глобального объекта, и выполнение продолжается (хотя в современном JavaScript оно, вероятно, аварийно завершится в дальнейшем). Присваивания, которые могут случайно создать глобальную переменную, в строгом режиме выбрасывают исключение:
В-третьих, в строгом режиме попытки удалить неудаляемые свойства будут вызывать исключения (в то время как прежде такая попытка просто не имела бы эффекта):
Это уже не является проблемой в ECMAScript 2015 (баг 1041128).
Упрощение работы с переменными
Строгий режим упрощает сопоставление имени переменной с местом её определения в коде. Многие оптимизации времени компиляции полагаются на возможность считать, что переменная X хранится в этом конкретном месте исходного кода. Иногда, при компиляции JavaScript простое сопоставление имени переменной с местом её определения в коде не возможно, без выполнения того самого кода. Строгий же режим исключает большинство таких случаев, благодаря чему оптимизации компилятора работают эффективнее.
В-третьих, строгий режим запрещает удаление простых имён. delete name в строгом режиме является синтаксической ошибкой:
Упрощение eval и arguments
Во-первых, ключевые слова eval и arguments не могут быть переопределены или изменены. Все подобные попытки это сделать являются синтаксическими ошибками:
«Безопасный» JavaScript
Строгий режим упрощает написание «безопасного» JavaScript-кода. Сейчас некоторые веб-сайты предоставляют пользователям возможность писать JavaScript, который будет выполняться на сайте от имени других пользователей. В браузерах, JavaScript может иметь доступ к приватной информации пользователя, поэтому, в целях ограничения доступа к запретной функциональности, такой JavaScript перед выполнением должен быть частично преобразован. Гибкость JavaScript делает это практически невозможным без многочисленных проверок во время исполнения. Функционал, исполняемый языком иногда столь массивен, что выполнение любых дополнительных проверок во время исполнения скрипта приведёт к значительной потере производительности. Однако, некоторые особенности строгого режима, плюс обязательное требование того, чтобы JavaScript, загруженный пользователем, имел строгий режим и вызывался определённым способом, существенно снижают потребность в таких проверках.
В-третьих, в функциях строгого режима свойство arguments больше не предоставляет доступ к переменным, созданным внутри функции. В некоторых предыдущих реализациях ECMAScript arguments.caller представлял собой объект, свойства которого являлись ссылками на переменные, созданные внутри функции при её вызове. Это представляет собой угрозу безопасности, так как нарушает возможность скрывать приватные данные внутри функций (замыканий). Также это делает невозможными большинство оптимизаций. Исходя из этих причин, ни один из современных браузеров не реализует этого поведения. Но всё же, ввиду своей исторической функциональности, arguments.caller для функций в строгом режиме всё ещё является неудаляемым свойством, которое вызывает исключение при попытке его чтения или записи:
Подготовка почвы для будущих версий ECMAScript
В будущих версиях ECMAScript с высокой вероятностью появится новый синтаксис, и для упрощения перехода на новые версии, в строгом режиме ECMAScript 5 введено несколько ограничений. Всегда проще вносить изменения в стандарт, если заранее подготовить для них основу в строгом режиме.
Во-вторых, в строгом режиме запрещается объявление функций глубже самого верхнего уровня скрипта или функции. В обычном коде в браузерах, объявление функций позволено «везде», что не является частью ES5 (или даже ES3!) Это расширение различных браузеров, не имеющее общего совместимого подхода. Есть надежда, что в последующих редакциях ECMAScript будет определена новая семантика для объявления функций вне верхнего уровня скрипта или функции. Запрет на объявление таких функций в строгом режиме производит «зачистку» для спецификации в будущем релизе ECMAScript:
Данный запрет не является особенностью строгого режима, потому что такое объявление функций является одним из расширений основного ES5. Но это рекомендация комитета ECMAScript, и браузеры реализуют её.
Строгий режим в браузерах
В большинстве браузеров в настоящее время строгий режим реализован. Однако не стоит впадать в слепую зависимость от него, потому что существует множество Версий браузеров, поддерживающих строгий режим лишь частично или вовсе не поддерживающих оный (например, Internet Explorer ниже версии 10!). Строгий режим изменяет семантику. Надежда на эти изменения приведёт к ошибкам и погрешностям в браузерах, в которых строгий режим не реализован. Проявляйте осторожность при использовании строгого режима, и подкрепляйте надёжность строгого режима тестами особенностей, которые проверяют, насколько верно реализованы его фрагменты. Наконец, старайтесь тестировать свой код в браузерах, как поддерживающих, так и не поддерживающих строгий режим. Если вы проводите тестирование только в тех браузерах, которые не поддерживают строгий режим, то вполне вероятно у вас появятся проблемы в браузерах, его поддерживающих, и наоборот.
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 будет применен для всего кода:
В качестве альтернативы вы можете включить Strict Mode только в отдельной функции, добавив «use strict» в начало тела вашей функции:
Наследуют ли внутренние функции Strict Mode от внешних функций?
Внутренняя функция, объявленная внутри внешней, в которой включен Strict Mode тоже будет иметь Strict Mode:
Важно запомнить, что Strict Mode не распространяется на «нестрогие» (ориг. non-strict) функции, которые выполняются внутри строгой функции (или они отправлены в функцию в качестве аргументов или выполняются, используя call или apply):
Почему я не могу включить Strict Mode в консоли моего браузера?
Когда выполняешь код в консоли фаербага или в других консолях использование «use strict» вне функции не имеет силы. Это потому, что большинство консолей обрамляют ваш код в eval’ом, поэтому ваш «use strict» не является первым выражением. Это можно обойти, обрамив ваш код в замыкание (IIFE), в начало которого мы положим «use strict» (но, когда я тестировал такой способ включения Strict Mode я понял, что это довольно неудобно, особенно если работать в консоли webkit developer tools — лучше тестировать ваш код на странице):
Что произойдет если мой браузер не поддерживает 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). Сейчас самое время начать эксперименты!
Строгий режим Javascript
Директива «use strict»; указывает, что код JavaScript должен выполняться в «строгом режиме».
Директива «use strict» была добавлена в JavaScript 1.8.5 (ECMAScript версии 5).
Это не оператор, а константное выражение, которое игнорируется более ранними версиями JavaScript.
Цель директивы «use strict» — указать, что код должен выполняться в, так называемом, «строгом режиме».
В строгом режиме вы не можете, например, использовать не декларированные переменные.
Строгий режим поддерживается в:
Декларирование строгого режима
Строгий режим декларируется путем добавления директивы «use strict»; в начало скрипта или функции.
Если директива указана в начале скрипта, то она имеет глобальный эффект (весь код скрипта выполняется в строгом режиме):
Если директива указана внутри функции, то она имеет локальный эффект (только код внутри функции выполняется в строгом режиме):
Синтаксис директивы «use strict»;
Синтаксис директивы, декларирующей строгий режим, разработан таким образом, чтобы была совместимость со старыми версиями JavaScript.
Компилирование числовых (4 + 5;) или строковых («John Doe»;) констант в программе JavaScript не имеет побочных эффектов. Они просто компилируются в несуществующую переменную и умирают.
Таким образом, строковая константа «use strict»; срабатывает только в новых компиляторах, которые «понимают» ее значение.
Зачем нужен строгий режим?
Благодаря строгому режиму проще писать «безопасный» JavaScript код.
В строгом режиме ранее вполне приемлемый «плохой синтаксис» превращается в реальные ошибки.
К примеру, в обычном режиме JavaScript опечатка в имени переменной приводит к созданию новой глобальной переменной. В строгом режиме это приведет к ошибке, что защищает от случайного создания глобальных переменных.
В обычном режиме JavaScript разработчик не получит никакого сообщения об ошибке, если попытается присвоить какое-либо значение свойствам, не предназначенным для записи.
В строгом режиме любая попытка присвоить какое-либо значение не предназначенному для записи свойству, свойству, определенному, как только для чтения, несуществующей переменной или несуществующему объекту приведет к возникновению ошибки.
Ограничения в строгом режиме
Нельзя использовать переменные без декларирования:
Внимание! Объекты тоже переменные.
Нельзя использовать объекты без декларирования:
Нельзя удалять переменную (или объект):
Нельзя удалять функцию:
Одинаковые имена параметров запрещены:
Восьмеричные числовые константы запрещены:
Восьмеричные экранированные символы запрещены:
Запись в свойства, предназначенные только для чтения, запрещено:
Запись в свойства, предназначенные только для возврата значения, запрещено:
Нельзя удалять неудаляемые свойства:
Нельзя использовать строку «eval» в качестве имени переменной:
Нельзя использовать строку «arguments» в качестве имени переменной:
Нельзя использовать выражение with:
По соображениям безопасности, функции eval() запрещено создавать переменные в области видимости, где она была вызвана:
В вызовах функций как f(), значением this был глобальный объект. В строгом режиме оно undefined.
Задел на будущее
В строгом режиме нельзя использовать будущие зарезервированные слова. Это implements, interface, let, package, private, protected, public, static, yield.
ВНИМАНИЕ!
Директива «use strict» распознается только в начале скрипта или функции.
Руководство по JavaScript, часть 7: строгий режим, ключевое слово this, события, модули, математические вычисления
Сегодня, в седьмой части перевода руководства по JavaScript, мы поговорим о выполнении кода в строгом режиме, об особенностях ключевого слова this, о событиях, о модулях, о математических вычислениях. Здесь же мы затронем темы работы с таймерами и асинхронного программирования.
Строгий режим
Строгий режим (strict mode) появился в стандарте ES5. В этом режиме меняется семантика языка, он нацелен на то, чтобы улучшить поведение JavaScript, что приводит к тому, что код в этом режиме ведёт себя не так, как обычный. Фактически, речь идёт о том, что в этом режиме устраняются недостатки, неоднозначности языка, устаревшие возможности, которые сохраняются в нём из соображений совместимости.
▍Включение строгого режима
Для того чтобы использовать в некоем коде строгий режим, его нужно явным образом включить. То есть, речь не идёт о том, что этот режим применяется по умолчанию. Такой подход нарушил бы работу бесчисленного количества существующих программ, опирающихся на механизмы языка, присутствовавшие в нём с самого начала, с 1996 года. На самом деле, значительные усилия тех, кто разрабатывает стандарты JavaScript, направлены именно на обеспечение совместимости, на то, чтобы код, написанный в расчёте на старые версии стандартов, можно было бы выполнять на сегодняшних JS-движках. Такой подход можно считать одним из залогов успеха JavaScript как языка для веб-разработки.
Для того чтобы включить строгий режим, используется особая директива, которая выглядит так.
Строгий режим может быть включён и на уровне отдельной функции. Для этого соответствующую директиву надо поместить в начале кода тела функции.
Подобное может оказаться полезным в том случае, если строгий режим нужно использовать в существующей кодовой базе и при этом включение его на уровне файла оказывается нецелесообразным по причине нехватки времени на тщательное тестирование кода всего этого файла.
Надо отметить, что, если строгий режим включён, выключить его во время выполнения программы нельзя.
Рассмотрим некоторые особенности строгого режима.
▍Борьба со случайной инициализацией глобальных переменных
Мы уже говорили о том, что если случайно назначить некое значение необъявленной переменной, даже если сделать это в коде функции, такая переменная по умолчанию будет сделана глобальной (принадлежащей глобальному объекту). Это может привести к неожиданностям.
Например, следующий код приводит к созданию именно такой переменной.
Переменная variable будет доступна в глобальной области видимости после выполнения IIFE.
Если включить на уровне этой функции строгий режим, тот же самый код вызовет ошибку.
▍Ошибки, возникающие при выполнении операций присваивания значений
JavaScript, в обычном режиме, никак не сообщает о некоторых ошибках, возникающих в ходе выполнения операций присваивания значений.
В JavaScript можно задавать свойства объектов с использованием метода Object.defineProperty(). В частности, с помощью этого метода можно задавать свойства, которые нельзя менять.
То же самое относится и к геттерам. Этот код выполнится, хотя и безрезультатно.
А попытка выполнить то же самое в строгом режиме вызовет ошибку, сообщающая о попытке установки свойства объекта, у которого есть лишь геттер.
В JavaScript есть метод Object.preventExtensions(), делающий объект нерасширяемым, то есть таким, к которому нельзя добавить новые свойства. При работе с такими объектами в обычном режиме проявляются те же особенности языка, которые мы рассматривали выше.
В эту же категорию действий, не приводящих к каким-то изменениям, возможно, ожидаемым программистом, но и не вызывающих ошибок, попадают операции, в ходе выполнения которых делаются попытки назначить некие свойства примитивным значениям. Например, такой код, в обычном режиме, не вызовет ошибки, но и не даст никаких результатов.
▍Ошибки, связанные с удалением сущностей
В обычном режиме, если попытаться удалить, помощью оператора delete, свойство объекта, которое удалить нельзя, delete просто возвратит false и всё тихо закончится неудачей.
В строгом режиме здесь будет выдана ошибка.
▍Аргументы функций с одинаковыми именами
Функции могут иметь параметры с одинаковыми именами, ошибок это не вызывает (хотя подобное выглядит как ошибка того, кто такую функцию создал).
Кстати, если при объявлении стрелочной функции её параметры будут иметь одинаковые имена, это, и в обычном режиме, приведёт к выводу сообщения об ошибке.
▍Восьмеричные значения
▍Оператор with
Особенности ключевого слова this
▍Ключевое слово this в строгом режиме
▍Ключевое слово this в методах объектов
Метод — это функция, ссылка на которую записана в свойство объекта. Ключевое слово this в такой функции ссылается на этот объект. Это утверждение можно проиллюстрировать следующим примером.
Обратите внимание на то, что вышеприведённый способ объявления метода объекта аналогичен такому:
То же самое поведение ключевого слова this в методе объекта можно наблюдать и при использовании следующей конструкции.
▍Ключевое слово this и стрелочные функции
Попробуем переписать вышеприведённый пример с использованием, в качестве метода объекта, стрелочной функции.
▍Привязка this
▍О привязке this в обработчиках событий браузера
События
JavaScript в браузере использует событийную модель программирования. Те или иные действия выполняются кодом в ответ на происходящие события. В этом разделе мы поговорим о событиях и о том, как их обрабатывать.
В качестве события может выступать, например, завершение загрузки DOM, получение данных, выполненное в результате асинхронного запроса, щелчок мышью по элементу страницы, прокрутка страницы, ввод некоего символа с клавиатуры. На самом деле, существует множество событий, обрабатывая которые, JS-код страницы позволяет решать широкий спектр задач по взаимодействию приложения с пользователями, с элементами страницы, с окружением, в котором работает код.
▍Обработчики событий
Реагировать на события можно с помощью обработчиков событий (event handler), которые представляют собой функции, вызываемые в тот момент, когда происходят события.
При необходимости для обработки одного и того же события можно зарегистрировать несколько обработчиков, которые будут вызываться в том случае, если это событие произойдёт. Регистрировать обработчики событий можно различными способами. Рассмотрим три таких способа.
▍Встроенные обработчики событий
В HTML-коде кнопки Button 2 применяется похожий подход, но здесь указывается функция, код которой выполняется в ответ на нажатие кнопки. Этот код выполняет вывод заданной строки в консоль и выводит окно с тем же текстом.
▍Назначение обработчика свойству HTML-элемента
Этот метод назначения обработчиков событий подходит для случаев, когда у некоего события элемента должен быть лишь один обработчик. Заключается он в назначении функции соответствующему свойству элемента.
Проверить, назначен ли обработчик некоему событию, можно так.
▍Использование метода addEventListener()
▍О назначении обработчиков событий различным элементам
▍Объект Event
В этом же примере, для обработки событий, связанных с мышью, используется объект MouseEvent. В обработчике события mousedown мы выводим в консоль тип события, номер кнопки (свойство button ) и координаты указателя в момент щелчка (свойства clientX и clientY ).
Объект DragEvent применяется при обработке событий, возникающих при перетаскивании элементов страницы.
▍Всплытие событий
Рассмотрим следующий пример.
Если открыть загрузить страницу с таким кодом в браузер, открыть консоль и последовательно щёлкнуть мышью сначала в свободной области страницы, потом — по синему прямоугольнику, а потом — по зелёному, то в консоль попадёт следующее:
То, что здесь можно наблюдать, называется всплытием события (event bubbling). А именно, событие, возникающее у дочернего элемента, распространяется на родительский элемент. Этот процесс продолжается до тех пор, пока событие не достигнет самого «верхнего» элемента. Если у элементов, по которым проходит всплывающее событие, определены соответствующие обработчики, они будут вызваны в соответствии с порядком распространения события.
▍Часто используемые события
Рассмотрим некоторые события, обработка которых нужна чаще всего.
Событие load
События мыши
События клавиатуры
Событие scroll
Ограничение частоты выполнения вычислений в обработчиках событий
События mousemove и scroll дают сведения о координатах мыши и о позиции прокрутки. Выполнение в обработчиках таких событий каких-то серьёзных вычислений может привести к замедлению работы программы. В подобной ситуации есть смысл задуматься об ограничении частоты выполнения таких вычислений. Этот приём называют «троттлингом» (throttling), его реализации можно найти в специализированных библиотеках вроде Lodash. Сущность этого приёма заключается в создании механизма, который позволяет ограничить частоту выполнения неких действий, которые, без ограничения, выполнялись бы слишком часто. Рассмотрим собственную реализацию этого механизма.
ES-модули
В стандарте ES6 появилась новая возможность, получившая название ES-модули. Потребность в стандартизации этой возможности назрела уже давно, что выражается в том, что и разработчики клиентских частей веб-проектов, и серверные программисты, пишущие для среды Node.js, уже давно нечто подобное используют.
Модуль представляет собой файл, содержащий некий код. Из этого файла можно экспортировать, делать общедоступными, функции и переменные. Ими можно воспользоваться, просто подключив модуль к некоему файлу с кодом, при этом внутренние механизмы модуля извне недоступны.
В Node.js в качестве системы модулей долгое время использовался и продолжает использоваться стандарт CommonJS. В браузерах, до появления ES-модулей, применялись различные библиотеки и системы сборки проектов, имитирующие возможность работы с модулями. Теперь же, после стандартизации, браузеры постепенно вводят поддержку ES-модулей, что позволяет говорить о том, что, для поддержки модулей, уже довольно скоро дополнительных средств не понадобится. В частности, по информации ресурса caniuse.com, в конце ноября 2018 года уровень поддержки ES-модулей браузерами немного превышает 80%.
Работа по внедрению ES-модулей ведётся и в Node.js.
▍Синтаксис ES-модулей
В Node.js для подключения ES-модулей применяется такая запись.
При работе с CommonJS-модулями то же самое выглядит так.
Здесь в модуле задана команда экспорта по умолчанию, поэтому экспортироваться может анонимная функция. В противном случае экспортируемым сущностям надо давать имена.
Теперь этот модуль можно импортировать в некий код (в другой модуль, например) и воспользоваться там его возможностями.
Загрузить модуль на HTML-страницу можно, используя тег
Обратите внимание на то, что такой способ импорта модулей работает как отложенная (defer) загрузка скрипта. Кроме того, важно учитывать то, что в нашем примере в модуле uppercase.js используется экспорт по умолчанию, поэтому, при его импорте, ему можно назначить любое желаемое имя. Вот как это выглядит в коде веб-страницы. Для того чтобы у вас этот пример заработал, вам понадобится локальный веб-сервер. Например, если вы пользуетесь редактором VSCode, можно воспользоваться его расширением Live Server (идентификатор — ritwickdey.liveserver).
Модули можно импортировать и с использованием абсолютного URL.
▍Другие возможности импорта и экспорта
Выше мы приводили пример модуля, использующего экспорт по умолчанию.
Однако из модуля можно экспортировать и несколько сущностей.
Импортировать из модуля можно только то, что нужно, пользуясь конструкциями следующего вида.
Пользоваться тем, что импортировано, можно напрямую.
Импортируемые сущности можно переименовывать:
Вот как его импортировать и использовать.
Пример работы с модулем можно посмотреть здесь.
При загрузке модулей используется CORS. Это означает, что для успешной загрузки модулей с других доменов у них должен быть задан заголовок CORS, разрешающий межсайтовую загрузку скриптов (наподобие Access-Control-Allow-Origin: * ).
▍Атрибут nomodule
▍О модулях ES6 и WebPack
Модули ES6 — это замечательная технология, привлекающая тем, что она входит в стандарт ECMAScript. Но, пользуясь модулями, нужно стремиться к тому, чтобы количество загружаемых файлов было бы небольшим, иначе это может сказаться на производительности загрузки страницы. В этой связи стоит сказать, что бандлер WebPack, используемый для упаковки кода приложений, по видимому, ещё долго не потеряет актуальности.
▍Модули CommonJS
Как уже было сказано, в Node.js используется система модулей CommonJS. Эта система позволяет разработчику создавать небольшие самостоятельные фрагменты кода, подходящие для использования во множестве проектов и поддающиеся автономному тестированию. На основе CommonJS создана огромнейшая экосистема модулей npm.
Давайте напишем CommonJS-модуль, основываясь на примере, который мы уже рассматривали. А именно, поместим в файл up-node.js следующий код.
Для того чтобы воспользоваться этим модулем в некоей программе, его нужно подключить.
Обычно пакеты, загружаемые из npm, импортируют так, как показано ниже.
Модули CommonJS загружаются синхронно и обрабатываются в том порядке, в котором осуществляется обнаружение в коде соответствующих команд. Эта система не используется в клиентском коде.
Из CommonJS-модуля можно экспортировать несколько сущностей.
Импортировать их можно следующим образом, используя возможности по деструктурирующему присваиванию.
Математические вычисления
▍Арифметические операторы
Сложение (+)
Оператор + выполняет сложение чисел и конкатенацию строк. Вот примеры его использования с числами:
Вот как он ведёт себя со строками, преобразуя, при необходимости, другие типы данных к строковому типу.
Вычитание (-)
Деление (/)
При работе с обычными числами оператор деления ведёт себя вполне ожидаемым образом.
Остаток от деления (%)
Оператор % возвращает остаток от деления, в некоторых ситуациях это может оказаться полезным.
Остатком от деления на 0 является особое значение NaN (Not a Number — не число).
Умножение (*)
Возведение в степень (**)
Этот оператор возводит первый операнд в степень, заданную вторым операндом.
▍Унарные операторы
Инкремент (++)
Унарный оператор ++ можно использовать для прибавления 1 к некоему значению. Его можно размещать до инкрементируемого значения или после него.
Если же поставить его после переменной — то он сначала вернёт её предыдущее значение, а потом уже увеличит.
Декремент (—)
Унарный оператор (-)
Такой оператор позволяет делать положительные числа отрицательными и наоборот.
Унарный оператор (+)
Этот оператор, если операнд не является числом, пытается преобразовать его к числу. Если оператор уже является числом — ничего не происходит.
▍Оператор присваивания и его разновидности
Его можно прочитать так: «Прибавить к значению переменной, расположенной слева, то, что находится справа, и записать результат сложения в ту же переменную». Фактически, вышеприведённый пример можно переписать следующим образом.
По такому же принципу работают и другие подобные операторы:
▍Приоритет операторов
При работе со сложными выражениями нужно учитывать приоритет операторов. Например, рассмотрим следующее выражение.
Для переопределения порядка выполнения операторов соответствующие части выражения можно включить в круглые скобки. Рассмотрим следующее выражение.
▍Объект Math
Объект Math содержит свойства и методы, предназначенные для упрощения математических вычислений. Подробности о нём можно почитать здесь. Этот объект используется самостоятельно, без создания экземпляров.
Среди его свойств можно, например, отметить Math.E — константу, содержащую число e, и Math.PI — константу, содержащую число π.
Вот список некоторых полезных методов этого объекта.
▍Сравнение значений
Для сравнения значений в JavaScript используются операторы сравнения, с некоторыми из которых мы уже встречались.
Таймеры и асинхронное программирование
В соответствующих разделах руководства, перевод которого мы публикуем, поднимаются темы использования таймеров и асинхронного программирования. Эти темы были рассмотрены в ранее опубликованном нами переводе курса по Node.js. Для того чтобы с ними ознакомиться, рекомендуем почитать следующие материалы:
Итоги
В этом материале мы поговорили о строгом режиме, об особенностях ключевого слова this, о событиях, о модулях, о математических вычислениях. В следующий раз обсудим новшества, которые принёс в язык стандарт ES6.
Уважаемые читатели! Встречались ли вы с ошибками, которые вызваны использованием оператора нестрогого равенства в JavaScript?