Введение в javascript

Способы записи числа

Представьте, что нам надо записать число 1 миллиард. Самый очевидный путь:

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

В JavaScript можно использовать букву , чтобы укоротить запись числа. Она добавляется к числу и заменяет указанное количество нулей:

Другими словами, производит операцию умножения числа на 1 с указанным количеством нулей.

Сейчас давайте запишем что-нибудь очень маленькое. К примеру, 1 микросекунду (одна миллионная секунды):

Записать микросекунду в укороченном виде нам поможет .

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

Другими словами, отрицательное число после подразумевает деление на 1 с указанным количеством нулей:

Шестнадцатеричные числа широко используются в JavaScript для представления цветов, кодировки символов и многого другого. Естественно, есть короткий стиль записи: , после которого указывается число.

Например:

Не так часто используются двоичные и восьмеричные числа, но они также поддерживаются для двоичных и для восьмеричных:

Есть только 3 системы счисления с такой поддержкой. Для других систем счисления мы рекомендуем использовать функцию (рассмотрим позже в этой главе).

Микрошаблоны

Микрошаблоны (англ. microtemplate) мы уже видели на примере .

Это HTML со вставками переменных и произвольным JS.

Пример:

Шаблонная система компилирует этот код в JavaScript-функцию с минимальными модификациями, и она уже, запустившись с данными, генерирует результат.

Достоинства и недостатки такого подхода:

Недостатки

  • Жёстко привязан к языку JavaScript.
  • При ошибке в шаблоне приходится лезть внутрь «страшной» функции

Достоинства

  • Простая и быстрая шаблонная система
  • Внутри JS-функции доступен полноценный браузерный отладчик, функция хоть и страшна, но понятна.

Включение произвольного JS-кода в шаблон, в теории, позволяет делать в нём всё, что угодно. Но обратная сторона медали – шаблон вместо внятного HTML может стать адским нагромождением разделителей вперемешку с вычислениями. Что рекомендуется делать в шаблонах, а что нет?

Можно разделить код на два типа с точки зрения шаблонизации:

  • Бизнес-логика – код, формирующий данные, основной код приложения.
  • Презентационная логика – код, описывающий, как показываются данные.

Например, код, получающий данные с сервера для вывода в таблице – бизнес-логика, а код, форматирующий даты для вывода – презентационная логика.

В шаблонах допустима лишь презентационная логика.

Зачастую, нужно использовать один и тот же шаблон и в браузере и на сервере.

Например, серверный код генерирует HTML со списком сообщений, а JavaScript на клиенте добавляет к нему новые по мере появления.

…Но как использовать на сервере шаблон с JavaScript, если его основной язык – PHP, Ruby, Java?

Эту проблему можно обойти. На сервер, использующем PHP, Ruby, Java или какой-то другой язык, дополнительно ставится виртуальная машина V8 и настраивается интеграция с ней. Почти все платформы это умеют.

После этого становится возможным запускать JavaScript-шаблоны и передавать им данные в виде объектов, массивов и так далее.

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

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

То есть, можно заранее, до выкладывания сайта на «боевой сервер», обработать шаблоны, создать из них JS-функции, объединить их в единый файл и далее, в «боевом окружении» использовать уже их.

Современные системы сборки (brunch, grunt с плагинами и другие) позволяют делать это удобно, а также хранить шаблоны в разных файлах, каждый – в нужной директории с JS-кодом для виджета.

Неточные вычисления

Внутри JavaScript число представлено в виде 64-битного формата IEEE-754. Для хранения числа используется 64 бита: 52 из них используется для хранения цифр, 11 из них для хранения положения десятичной точки (если число целое, то хранится 0), и один бит отведён на хранение знака.

Если число слишком большое, оно переполнит 64-битное хранилище, JavaScript вернёт бесконечность:

Наиболее часто встречающаяся ошибка при работе с числами в JavaScript – это потеря точности.

Посмотрите на это (неверное!) сравнение:

Да-да, сумма и не равна .

Странно! Что тогда, если не ?

Но почему это происходит?

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

Другими словами, что такое ? Это единица делённая на десять — , одна десятая. В десятичной системе счисления такие числа легко представимы, по сравнению с одной третьей: , которая становится бесконечной дробью .

Деление на гарантированно хорошо работает в десятичной системе, но деление на – нет. По той же причине и в двоичной системе счисления, деление на обязательно сработает, а становится бесконечной дробью.

В JavaScript нет возможности для хранения точных значений 0.1 или 0.2, используя двоичную систему, точно также, как нет возможности хранить одну третью в десятичной системе счисления.

Числовой формат IEEE-754 решает эту проблему путём округления до ближайшего возможного числа. Правила округления обычно не позволяют нам увидеть эту «крошечную потерю точности», но она существует.

Пример:

И когда мы суммируем 2 числа, их «неточности» тоже суммируются.

Вот почему – это не совсем .

Не только в JavaScript

Справедливости ради заметим, что ошибка в точности вычислений для чисел с плавающей точкой сохраняется в любом другом языке, где используется формат IEEE 754, включая PHP, Java, C, Perl, Ruby.

Можно ли обойти проблему? Конечно, наиболее надёжный способ — это округлить результат используя метод toFixed(n):

Также можно временно умножить число на 100 (или на большее), чтобы привести его к целому, выполнить математические действия, а после разделить обратно. Суммируя целые числа, мы уменьшаем погрешность, но она все равно появляется при финальном делении:

Таким образом, метод умножения/деления уменьшает погрешность, но полностью её не решает.

Забавный пример

Попробуйте выполнить его:

Причина та же – потеря точности. Из 64 бит, отведённых на число, сами цифры числа занимают до 52 бит, остальные 11 бит хранят позицию десятичной точки и один бит – знак. Так что если 52 бит не хватает на цифры, то при записи пропадут младшие разряды.

Интерпретатор не выдаст ошибку, но в результате получится «не совсем то число», что мы и видим в примере выше. Как говорится: «как смог, так записал».

Два нуля

Другим забавным следствием внутреннего представления чисел является наличие двух нулей: и .

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

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

Хелперы и фильтры

JavaScript-вставки не всегда просты и элегантны. Иногда, чтобы что-то сделать, нужно написать порядочно кода.

Для того, чтобы сделать шаблоны компактнее и проще, в них стали добавлять фильтры и хелперы.

  • Хелпер (англ. helper) – вспомогательная функция, которая доступна в шаблонах и используется для решения часто возникающих задач.

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

    Пример хелпера – функция , которая переводит на текущий язык:

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

  • Фильтр – это функция, которая трансформирует данные, например, форматирует дату, сортирует элементы массива и так далее.

    Обычно для фильтров предусмотрен специальный «особо простой и короткий» синтаксис.

    Например, в системе шаблонизации EJS, которая по сути такая же, но мощнее, чем , фильтры задаются через символ , внутри разделителя .

    Чтобы вывести с большой буквы, можно вместо написать . Чтобы выводить отсортированный массив, можно использовать и так далее.

Сложение строк, бинарный +

Давайте посмотрим специальные возможности операторов JavaScript, которые выходят за рамки школьной математики.

Обычно при помощи плюса складывают числа.

Но если бинарный оператор применить к строкам, то он их объединяет в одну:

Обратите внимание, если хотя бы один операнд является строкой, то второй будет также преобразован к строке. Например:

Например:

Причём не важно, справа или слева находится операнд-строка. Правило простое: если хотя бы один из операндов является строкой, то второй будет также преобразован к строке

Тем не менее, помните, что операции выполняются слева направо. Если перед строкой идут два числа, то числа будут сложены перед преобразованием в строку:

Сложение и преобразование строк – это особенность бинарного плюса . Другие арифметические операторы работают только с числами и всегда преобразуют операнды в числа.

Например, вычитание и деление:

Квадратные скобки

Для свойств, имена которых состоят из нескольких слов, доступ к значению «через точку» не работает:

JavaScript видит, что мы обращаемся к свойству , а затем идёт непонятное слово . В итоге синтаксическая ошибка.

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

Для таких случаев существует альтернативный способ доступа к свойствам через квадратные скобки. Такой способ сработает с любым именем свойства:

Сейчас всё в порядке.

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

Например, имя свойства может храниться в переменной:

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

Здесь переменная может быть вычислена во время выполнения кода или зависеть от пользовательского ввода. После этого мы используем её для доступа к свойству. Это даёт нам большую гибкость.

Пример:

Запись «через точку» такого не позволяет:

Мы можем использовать квадратные скобки в литеральной нотации для создания вычисляемого свойства.

Пример:

Смысл вычисляемого свойства прост: запись означает, что имя свойства необходимо взять из переменной .

И если посетитель введёт слово , то в объекте теперь будет лежать свойство .

По сути, пример выше работает так же, как и следующий пример:

…Но первый пример выглядит лаконичнее.

Мы можем использовать и более сложные выражения в квадратных скобках:

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

Подведём итог: в большинстве случаев, когда имена свойств известны и просты, используется запись через точку. Если же нам нужно что-то более сложное, то мы используем квадратные скобки.

Строковое преобразование

Строковое преобразование проще всего увидеть, если вывести объект при помощи :

Как видно, содержимое объекта не вывелось. Это потому, что стандартным строковым представлением пользовательского объекта является строка .

Такой вывод объекта не содержит интересной информации. Поэтому имеет смысл его поменять на что-то более полезное.

Если в объекте присутствует метод , который возвращает примитив, то он используется для преобразования.

Результатом может быть любой примитив

Метод не обязан возвращать именно строку.

Его результат может быть любого примитивного типа. Например, это может быть число, как в примере ниже:

Поэтому мы и называем его здесь «строковое преобразование», а не «преобразование к строке».

Все объекты, включая встроенные, имеют свои реализации метода , например:

Численное преобразование

Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений , ).

Для преобразования к числу в явном виде можно вызвать , либо, что короче, поставить перед выражением унарный плюс :

Значение Преобразуется в…
Строка Пробельные символы по краям обрезаются.Далее, если остаётся пустая строка, то , иначе из непустой строки «считывается» число, при ошибке результат .

Например:

Ещё примеры:

  • Логические значения:

  • Сравнение разных типов – значит численное преобразование:

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

  • С логическими значениями:

    Здесь сравнение снова приводит обе части к числу. В первой строке слева и справа получается , во второй .

Посмотрим на поведение специальных значений более внимательно.

Интуитивно, значения ассоциируются с нулём, но при преобразованиях ведут себя иначе.

Специальные значения преобразуются к числу так:

Значение Преобразуется в…

Это преобразование осуществляется при арифметических операциях и сравнениях

Это ведёт к забавным последствиям.

Например, не подчиняется законам математики – он «больше либо равен нулю»: , но не больше и не равен:

Значение вообще «несравнимо»:

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

Используйте в таких случаях переменные-числа или приводите к числу явно.

Однобуквенные переменные

Ещё один способ писать быстрее – использовать короткие имена переменных. Называйте их , или .

Короткая переменная прячется в коде лучше, чем ниндзя в лесу. Никто не сможет найти её, используя функцию «Поиск» текстового редактора. Более того, даже найдя – никто не сможет «расшифровать» её и догадаться, что она означает.

…Но есть одно исключение. В тех местах, где однобуквенные переменные общеприняты, например, в счётчике цикла – ни в коем случае не используйте стандартные названия , , . Где угодно, только не здесь!

Остановите свой взыскательный взгляд на чём-нибудь более экзотическом. Например, или .

Эффективность этого подхода особенно заметна, если тело цикла занимает одну-две страницы (чем длиннее – тем лучше).

В этом случае заметить, что переменная – счётчик цикла, без пролистывания вверх, невозможно.

Tensorflow.js

Библиотека от Google (преемница популярной deeplearnjs) дает возможность обучать нейронные сети в браузере или запускать предобученные модели в режиме вывода. Создатели библиотеки утверждают, что она может быть использована как NumPy для веба. Tensorflow.js с простым в работе API может быть использована в разнообразных полезных приложениях. Библиотека также активно поддерживается.

7. TensorFlow Deep Playground

Deep playground  — для интерактивной визуализации нейронных сетей, написанный на TypeScript с использованием d3.js. Хотя этот проект в основном содержит самую базовую площадку для tensorflow, он может быть использован для различных целей, например, в качестве очень выразительного обучающего инструмента.

Игровая площадка Tensorflow

8. Compromise

Compromise — популярная библиотека, которая позволяет осуществлять обработку естественного языка (NLP) при помощи Javascript. Она базовая, компилируется в единственный маленький файл. По некоторым причинам, скромного функционала вполне хватает для того, чтобы сделать Compromise главным кандидатом для использования практически в любом приложении, в котором требуется базовый NLP.

Compromise напоминает, как в действительности выглядит английский

9. Neuro.js

Этот проект представляет собой Javascript библиотеку глубокого обучения и обучения с подкреплением в браузере. Из-за реализации полнофункционального фреймворка машинного обучения на основе нейронных сетей с поддержкой обучения с подкреплением, Neuro.js считается преемником Conventjs.

Беспилотное авто с Neuro.js

10. mljs

Это группа репозиториев, содержащая инструменты для машинного обучения для Javascript, разработана группой mljs. Mljs включает в себя обучение с учителем и без, искусственные нейронные сети, алгоритмы регрессии и поддерживает библиотеки для статистики, математики тому подобное. Здесь можно найти краткое введение в тему.

Проект mljs на GitHub

11. Mind

Mind — гибкая нейросетевая библиотека для Node.js и браузера. Mind учится предсказывать, выполняя матричную обработку тренировочных данных и обеспечивая настраиваемую топологию сети. Можете использовать уже существующие разработки, что может быть весьма полезно для ваших приложений.

WeakMap и WeakSet

– особый вид , не препятствующий сборщику мусора удалять свои элементы. То же самое – для .

То есть, если некий объект присутствует только в – он удаляется из памяти.

Это нужно для тех ситуаций, когда основное место для хранения и использования объектов находится где-то в другом месте кода, а здесь мы хотим хранить для них «вспомогательные» данные, существующие лишь пока жив объект.

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

Если поместить такие данные в , а объект сделать ключом, то они будут автоматически удалены из памяти, когда удалится элемент.

Например:

Таким образом, избавляет нас от необходимости вручную удалять вспомогательные данные, когда удалён основной объект.

У WeakMap есть ряд ограничений:

  • Только объекты в качестве ключей.
  • Нет свойства .
  • Нельзя перебрать элементы итератором или .
  • Нет метода .

Иными словами, работает только на запись (, ) и чтение (, ) элементов по конкретному ключу, а не как полноценная коллекция. Нельзя вывести всё содержимое , нет соответствующих методов.

Это связано с тем, что содержимое может быть модифицировано сборщиком мусора в любой момент, независимо от программиста. Сборщик мусора работает сам по себе. Он не гарантирует, что очистит объект сразу же, когда это стало возможным. В равной степени он не гарантирует и обратное. Нет какого-то конкретного момента, когда такая очистка точно произойдёт – это определяется внутренними алгоритмами сборщика и его сведениями о системе.

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

То же самое относится и к : можно добавлять элементы, проверять их наличие, но нельзя получить их список и даже узнать количество.

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

Расширение встроенных элементов

Выше мы видели пример создания элемента на основе базового . Но можно расширить и другие, более конкретные HTML-элементы.

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

Например, кнопку:

Важные детали:

Прототип теперь наследует не от , а от
Чтобы расширить элемент, нужно унаследовать прототип от его класса.
В HTML указывается при помощи атрибута
Это принципиальное отличие разметки от обычного объявления без . Теперь работать не будет, нужно использовать исходный тег и .
Работают методы, стили и события кнопки.
При клике на кнопку её не отличишь от встроенной. И всё же, это новый элемент, со своими методами, в данном случае .

При создании нового элемента в JS, если используется , необходимо указать и исходный тег в том числе:

Новый элемент

Для описания нового элемента используется вызов .

Здесь:

  • – имя нового тега, например . Оно обязано содержать дефис . Спецификация требует дефис, чтобы избежать в будущем конфликтов со стандартными элементами HTML. Нельзя создать элемент или – будет ошибка.
  • – объект-прототип для нового элемента, он должен наследовать от , чтобы у элемента были стандартные свойства и методы.

Вот, к примеру, новый элемент :

Использовать новый элемент в HTML можно и до его объявления через .

Для этого в браузере предусмотрен специальный режим «обновления» существующих элементов.

Если браузер видит элемент с неизвестным именем, в котором есть дефис (такие элементы называются «unresolved»), то:

  • Он ставит такому элементу специальный CSS-псевдокласс , для того, чтобы через CSS можно было показать, что он ещё «не подгрузился».
  • При вызове такие элементы автоматически обновятся до нужного класса.

В примере ниже регистрация элемента происходит через 2 секунды после его появления в разметке:

Можно создавать такие элементы и в JavaScript – обычным вызовом :

Копирование по ссылке

Примитивные типы: строки, числа, логические значения – присваиваются и копируются «по значению».

Например:

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

Объекты ведут себя иначе.

Переменная хранит не сам объект, а его «адрес в памяти», другими словами «ссылку» на него.

Проиллюстрируем это:

Сам объект хранится где-то в памяти. А в переменной лежит «ссылка» на эту область памяти.

Когда переменная объекта копируется – копируется ссылка, сам же объект не дублируется.

Если мы представляем объект как ящик, то переменная – это ключ к нему. Копирование переменной дублирует ключ, но не сам ящик.

Например:

Теперь у нас есть две переменные, каждая из которых содержит ссылку на один и тот же объект:

Мы можем использовать любую из переменных для доступа к ящику и изменения его содержимого:

Приведённый выше пример демонстрирует, что объект только один. Как если бы у нас был один ящик с двумя ключами и мы использовали один из них (), чтобы войти в него и что-то изменить, а затем, открыв ящик другим ключом (), мы бы увидели эти изменения.

Операторы равенства и строгого равенства для объектов работают одинаково.

Два объекта равны только в том случае, если это один и тот же объект.

Например, две переменные ссылаются на один и тот же объект, они равны:

В примере ниже два разных объекта не равны, хотя и оба пусты:

Для сравнений типа или для сравнения с примитивом объекты преобразуются в примитивы.

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

Объект, объявленный через , может быть изменён.

Пример:

Может показаться, что строка должна вызвать ошибку, но нет, здесь всё в порядке. Дело в том, что объявление защищает от изменений только само значение . А в нашем случае значение – это ссылка на объект, и это значение мы не меняем. В строке мы действуем внутри объекта, мы не переназначаем .

Если же мы попытаемся присвоить другое значение, то выдаст ошибку:

…Но что делать, если мы хотим сделать константами свойства объекта? Как сделать так, чтобы попытка изменить выдавала ошибку? Это тоже возможно. Мы рассмотрим эту тему в главе Флаги и дескрипторы свойств.

Шаблонизация компонент

До этого мы говорили о шаблонных системах «общего назначения». По большому счёту, это всего лишь механизмы для преобразования одной строки в другую. Но при описании шаблона для компоненты мы хотим сгенерировать не просто строку, а DOM-элемент, и не просто генерировать, а в дальнейшем – с ним работать.

Современные шаблонные системы «заточены» на это. Они умеют создавать по шаблону DOM-элементы и автоматически выполнять после этого разные полезные действия.

Например:

  • Можно сохранить важные подэлементы в свойства компоненты, чтобы было проще к ним обращаться из JavaScript.
  • Можно автоматически назначать обработчики из методов компонента.
  • Можно запомнить, какие данные относятся к каким элементам и в дальнейшем, при изменении данных автоматически обновлять DOM («привязка данных» – англ. data binding).

Одной из первых систем шаблонизации, которая поддерживает подобные возможности была Knockout.JS.

Попробуйте поменять значение в примере ниже и вы увидите двухстороннюю привязку данных в действии:

Библиотека Knockout.JS создаёт объект , который и содержит все её возможности.

В этом примере работу начинает вызов .

Его аргументы:

  • – объект с данными.
  • – DOM-элемент, который будет использован в качестве шаблона.

Он пробегает по всем подэлементам и, если видит атрибут , то читает его и выполняет привязку данных.

Значение означает, что нужно привязать к свойству объекта данных.

Привязка осуществляется в две стороны:

  1. Во-первых, библиотека ставит на свой обработчик (можно выбрать другие события, см. документацию), который будет обновлять . То есть, изменение автоматически меняет

  2. Во-вторых, свойство создано как . Технически, – это функция-обёртка вокруг значения: геттер-сеттер, который умеет рассылать события при изменении.

    Например:

    Библиотека Knockout.JS ставит свой обработчик на изменение значения и при этом обновляет все привязки. Так что при изменении меняется и .

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

Вызов можно делать внутри компоненты, и таким образом устанавливать соответствия между её объектом и DOM.

«var» обрабатываются в начале запуска функции

Объявления переменных обрабатываются в начале выполнения функции (или запуска скрипта, если переменная является глобальной).

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

Т.е. этот код:

…Технически полностью эквивалентен следующему (объявление переменной перемещено в начало функции):

…И даже коду ниже (как вы помните, блочная область видимости игнорируется):

Это поведение называется «hoisting» (всплытие, поднятие), потому что все объявления переменных «всплывают» в самый верх функции.

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

Объявления переменных «всплывают», но присваивания значений – нет.

Это проще всего продемонстрировать на примере:

Строка состоит из двух действий:

  1. Объявление переменной
  2. Присвоение значения в переменную .

Объявление переменной обрабатывается в начале выполнения функции («всплывает»), однако присвоение значения всегда происходит в той строке кода, где оно указано. Т.е. код выполняется по следующему сценарию:

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

В обоих примерах выше вызов происходил без ошибки, потому что переменная уже существовала. Но её значение ещё не было присвоено, поэтому мы получали .

Map

– коллекция для хранения записей вида .

В отличие от объектов, в которых ключами могут быть только строки, в ключом может быть произвольное значение, например:

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

Свойство хранит общее количество записей в .

Метод можно чейнить:

При создании можно сразу инициализировать списком значений.

Объект с тремя ключами, как и в примере выше:

Аргументом должен быть итерируемый объект (не обязательно именно массив). Везде утиная типизация, максимальная гибкость.

В качестве ключей можно использовать и объекты:

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

Как map сравнивает ключи

Для проверки значений на эквивалентность используется алгоритм . Он аналогичен строгому равенству , отличие – в том, что считается равным . Поэтому значение также может быть использовано в качестве ключа.

Этот алгоритм нельзя изменять или задавать свою функцию сравнения.

Методы для удаления записей:

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

Для проверки существования ключа:

map.has(key) – возвращает true, если ключ есть, иначе false.

Для итерации по используется один из трёх методов:

  • – возвращает итерируемый объект для ключей,
  • – возвращает итерируемый объект для значений,
  • – возвращает итерируемый объект для записей , он используется по умолчанию в .

Например:

Перебор идёт в том же порядке, что и вставка

Перебор осуществляется в порядке вставки. Объекты гарантируют это, в отличие от обычных объектов .

Кроме того, у есть стандартный метод , аналогичный встроенному в массивы:

Зачем Custom Elements?

Критично настроенный читатель скажет: «Зачем ещё стандарт для своих типов элементов? Я могу создать любой элемент и прямо сейчас! В любом из современных браузеров можно писать любой HTML, используя свои теги: . Или создавать элементы из JavaScript при помощи .»

Однако, по умолчанию элемент с нестандартным названием (например ) воспринимается браузером, как нечто неопределённо-непонятное. Ему соответствует класс , и у него нет каких-либо особых методов.

Стандарт Custom Elements позволяет описывать для новых элементов свои свойства, методы, объявлять свой DOM, подобие конструктора и многое другое.

Давайте посмотрим это на примерах.

Для примеров рекомендуется Chrome

Так как спецификация не окончательна, то для запуска примеров рекомендуется использовать Google Chrome, лучше – последнюю сборку Chrome Canary, в которой, как правило, отражены последние изменения.

Ссылка на основную публикацию