45 вопросов по JavaScript с собеседований
Значения (values) в JavaScript имеют типы, а переменные, в которых лежат эти значения — не имеют.
Встроенные типы данных:
Оператор typeof
Оператор typeof получает некоторое значение и определяет его тип:
Приведение типов (coercion)
Конвертация встроенных типов данных друг в друга в JavaScript называется приведением типов (coercion). Оно бывает явное (explicit) и неявное (implicit).
Пример явного приведения:
Пример неявного приведения:
Особенно важно понимать, как происходит приведение к логическому типу, так как оно часто происходит в условиях.
Следующие значения приводятся к false:
- «» (пустая строка)
- 0, -0, NaN
- null, undefined
- false
- «hello» (любая непустая строка, даже » » — строка с пробелом)
- 42 (число, отличное от нуля)
- true
- [ ], [ 1, «2»] (любой массив, даже пустой)
- < >, < a: 42 >(любой объект, даже пустой)
- function foo() < >(любая функция)
Равенство (equality)
В JavaScript есть два вида сравнения:
- строгое (strict) без приведения типов ( === );
- абстрактное с приведением типов.
Сравнение осуществляется по некоторым простым правилам:
- Если значения (стороны сравнения) могут принимать значения true или false , избегайте нестрогого сравнения и используйте === ;
- Если в сравнении участвуют специфические значения ( 0 , «» , [] — пустой массив), избегайте нестрогого сравнения.
- В остальных случаях можно использовать == . Это безопасно и во многих случаях делает код более читаемым.
null и undefined
JavaScript (и TypeScript) имеют два специальных типа данных — null и undefined. Они предназначены для обозначения разных вещей (концепций):
- undefined — значение не инициализировано
- null — значение в данный момент недоступно
Ключевое слово let
Переменные, объявленные с помощью ключевого слова let , имеют блочную область видимости. Это значит, что они недоступны снаружи блока
Строгий режим (strict mode) и конструкция «use strict»
Строгий режим — фича стандарта ECMAScript 5. Размещая строку «use strict» в начале программы или функции, вы даете указание интерпретатору исполнять код в «строгом» контексте.
В строгом режиме действуют более жесткие правила к конструкциям языка и выбрасывается больше исключений. Например, переменная, объявленная без ключевого слова ( var , let , const ) в строгом режиме вызовет ошибку (в нестрогом будет создано новое свойство у глобального объекта).
Полифиллы и шимы (shim)
Шим — это любой фрагмент кода, который перехватывает обращение к API и добавляет уровень абстракции в приложение. Шимы существуют не только в вебе.
Полифилл — это шим для веба и браузерных API — специальный код (или плагин), который позволяет добавить некоторую функциональность в среду, которая эту функциональность по умолчанию не поддерживает (например, добавить новые функции JS в старые браузеры). Полифиллы не являются частью стандарта HTML5.
Транспилирование
Транспилирование, транспиляция — преобразование кода, написанного в новом стандарте в его эквивалент в старом стиле (ES6 в ES5).
Разница между ES5 и ES6
- ECMAScript 5 (ES5) — 5-е издание ECMAScript, стандартизированное в 2009 году. Поддерживается современными браузерами практически полностью.
- ECMAScript 6 (ECMAScript 2016, ES6) — 6-е издание ECMAScript, стандартизированное в 2015 году. Частично поддерживается большинством современных браузеров.
Несколько ключевых отличий двух стандартов:
- Стрелочные функции и интерполяция в строках.
- Ключевое слово const .Константы в JavaScript отличаются от констант в других языках программирования. Они сохраняют неизменной только ссылку на значение. Таким образом, вы можете добавлять, удалять и изменять свойства объявленного константным объекта, но не можете перезаписать текущую переменную, в которой лежит этот объект.
- Блочная видимость.Переменные, объявленные с помощью новых ключевых слов let и const имеют блочную область видимости, то есть недоступны за пределами <> -блоков. Кроме того, они не поднимаются, как var -переменные.
- Параметры по умолчанию.Теперь функцию можно инициализировать с дефолтным значением параметров. Оно будет использовано, если параметр не будет передан при вызове.
- Классы и наследование.Новый стандарт ввел в язык поддержку привычного синтаксиса классов ( class ), конструкторы ( constructor ) и ключевое слово extend для оформления наследования.
- Оператор for-of для перебора итерируемых объектов в цикле.
- spread -оператор, который удобно использовать для слияния объектов и еще во многих случаях.
- Обещания (Promises).Механизм для обработки результатов и ошибок асинхронных операций. По сути, это то же самое, что и коллбэки, но гораздо удобнее. Например, промисы можно чейнить (объединять в цепочки).
- Модули. Способ разбития кода на отдельные модули, которые можно импортировать при необходимости.
- Синтаксис экспорта позволяет выделить функциональность модуля:
Примитивные значения
Является ли число целым (integer)?
Очень простой способ проверить, имеет ли число дробную часть — разделить его на единицу и проверить остаток.
Почему 0.1 + 0.2 === 0.3 — это false ?
Действительно, в JavaScript 0.1 + 0.2 на самом деле равно 0.30000000000000004. Дело в том, что все числа в языке (даже целые) представлены в формате с плавающей запятой (float). В двоичной системе счисления эти числа — бесконечные дроби. Для их хранения выделяется ограниченный объем памяти, поэтому возникают подобные неточности.
Рекурсивная функция, возвращающая двоичное представление числа
Например, получив на вход число 4, эта функция должна вернуть 100.
Является ли число степенью двойки?
Обратите внимание, что 0 не является степенью двойки.
Решение очень простое, основано на побитовом умножении (И, & ). Этот оператор ставит 1 на бит результата, для которого соответствующие биты операндов равны 1.
Возьмем для примера 4 & 3 . Двоичное представление четверки — 100, тройки — 011. Ни в одном бите операнды не совпадают, поэтому результат будет 000.
Другой пример: 5 & 4 . В двоичном виде это 101 & 100 = 100 .
Суть решения в том, что любая степень двойки — это единственная единица и нули (2 -10, 4 — 100, 8 — 1000). Если вычесть из него единицу, полученное число будет состоять из одних единиц (1 — 1, 3 — 11, 7 — 111). То есть у самого числа (степень двойки) и этого же числа, уменьшенного на 1 все биты разные, значит, побитовое умножение в результате даст 0.
Развернуть все слова в полученной строке
Решим задачу с помощью одной простой функции в два этапа: сначала развернем все предложение целиком, затем вернем исходный порядок слов.
Строки-анаграммы
Определить, являются ли две строки анаграммами друг друга.
Строки-палиндромы
Палиндром — это слово, фраза, число или другая последовательность символов, которая читается одинаково слева направо и справа налево. Важно учитывать, что в строке могут быть пробелы и символы в разном регистре.
А вот этот вариант работает в 25 раз быстрее (если, конечно, вы сможете в нем разобраться):
Изоморфные строки
Изоморфными называются строки, между символами которых можно установить однозначное соответствие. Например, paper и title изоморфны ( p = t , a = i , e = l , r = e ). Количество и порядок символов при этом необходимо учитывать.
- egg и sad — неизоморфны,
- dgg и add — изоморфны.
Объекты и массивы
Объекты
Объекты — это составные структуры. Для них можно определить именованные свойства, в каждом из которых может содержаться значение любого типа.
Скобочная нотация также полезна, если вы хотите получить доступ к свойству, имя которого хранится в переменной:
Сравнение объектов
Непримитивные значения в JavaScript (объекты) передаются по ссылке, а операторы сравнения ( == и === ) сравнивают именно ссылки. Поэтому два разных объекта с идентичными свойствами не будут равны друг другу.
Есть одна маленькая хитрость, которой можно воспользоваться для сравнения простых структур. Если сравнить объект с примитивом (например, строкой), он будет приведет к строковому представлению. Для массива, например, строковое представление — это строка со списком элементов, разделенных запятыми. Благодаря этому можно сделать так:
Однако объекты, не являющиеся массивами, многоуровневые массивы, и объекты со сложными значениями свойств (DOM-элементы) таким образом сравнить не удастся.
Вместо приведения к строке также можно использовать сериализацию ( JSON.stringify(obj) ), но этот способ тоже работает не во всех случаях.
Для глубокого сравнения нужно использовать специальные библиотеки, например, deep-equal, или рекурсивный алгоритм.
Вот здесь можно увидеть реализацию более академичного способа сравнения объектов.
Массивы
Массивы — это объекты, которые хранят значения не в именованных свойствах, а в нумерованных.
Как очистить массив?
Способ 1. Присвоить переменной новый пустой массив:
Этот способ можно использовать, если ваш код никак не ссылается на оригинальный массив, так как при этом создается совершенно новый объект. Если в какой-то переменной есть ссылка на arr, то она не изменится.
Способ 2. Обнулить длину массива
Существующий массив очистится, так же как и все ссылки на него.
Способ 3. Вырезать лишние элементы
Работает так же, как предыдущий способ:
Способ 4. Удалять элементы по одному
Довольно громоздко, но может быть полезно, если вы хотите перед удалением что-нибудь еще сделать с элементами.
Является ли объект массивом?
Лучший способ узнать, является ли объект инстансом определенного класса, — использовать метод Object.prototype.toString() .
Таким образом, проверить, является ли полученное значение массивом можно с помощью конструкции:
В jQuery есть специальный метод $.isArray(value) , который под капотом использует ту же самую проверку.
Современные браузеры (Chrome 5, Firefox 4.0, IE 9, Opera 10.5 and Safari 5) поддерживают метод Array.isArray(value) .
Зачем вообще проверять, является ли значение массивом? Это очень полезно для перегрузки методов в зависимости от полученных параметров. Например, вы можете работать в одном и том же методе и с отдельной строкой, и с массивом строк. В каждом конкретном случае проверка на тип параметра может быть разной. Например, в приведенном примере проще будет воспользоваться оператором typeof :
Но проверка на массив тоже может потребоваться, ведь в функцию foo может быть передан, например, обычный объект, с которым она не умеет работать.
В массиве чисел найти максимальную разницу между двумя элементами, причем большее число должно обязательно стоять после меньшего
Обязательно разберитесь в условии прежде чем приступать к решению.
Если у нас есть массив:
то в ответе ожидается значение 11 (15 — 5 = 11), но не 14 (15 — 1 = 14), так как большее число должно находиться после меньшего.
Решение выглядит так:
Если алгоритм не очень понятен, обязательно разберитесь с ним на бумаге. Важно понимать, что мы не потеряем наибольшую разницу, обновляя минимальное значение, так как сравнивать его можно только со следующими большими числами.
Известно, что в неотсортированном массиве содержится (n-1) из n последовательных чисел (границы этого диапазона известны). Найдите пропущенное число за время O(n)
Тут важно разобраться в условии задачи. Если что-то непонятно, не стесняйтесь спрашивать.
У вас есть диапазон чисел от num1 до num2 включительно (обе границы известны). Длина диапазона — n .
Также есть массив длины n-1 , в котором содержатся все числа из диапазона, кроме одного. Вам нужно найти это единственное пропущенное число.
Так как лишних чисел в массиве нет, недостающее можно найти, подсчитав реальную и полную суммы элементов.
Время работы функции составляет O(n) так как каждый элемент перебирается всего 1 раз.
Удалить из массива повторяющиеся значения, вернуть только уникальные элементы
Эту задачу очень просто решить, используя возможности стандарта ES6 (сеты — коллекции с уникальными элементами):
Но и на ES5 ее вполне можно решить (и даже нужно!). Для этого используем обычный объект — и устанавливаем ему свойства с именами, равными элементам массива.
Реализуйте добавление в очередь и удаление из очереди с помощью двух стеков
Очереди элементов работают по принципу первый пришел — первый ушел.
Если мы добавляем элементы очереди в массив, чтобы получить самый первый, нужно воспользоваться методом unshift , но это довольно тяжелая операция, так как она сдвигает индексы всех оставшихся элементов массива.
В то же время существует более легкий метод pop , который берет последний элемент массива и не меняет порядок индексации.
Если у нас есть два массива, можно сделать так:
В stackInput элементы добавляются как в обычную очередь. Если нужно получить первый элемент, мы переворачиваем исходную очередь, там что первый элемент в stackInput становится последним в stackOutput. Забрать последний элемент из stackOutput (который, на самом деле, первый в очереди), можно с помощью метода pop .
Обратите внимание, пока в stackOutput есть хоть один элемент, мы ничего не переносим из исходной очереди, чтобы не нарушать порядок. Элементы там могут накапливаться и дальше. Когда output-стек обнулится, мы вновь скопируем в него накопившиеся элементы.
Больше методов массивов в JS вы можете найти здесь.
Найти пересечение массивов
Пересечение — это набор элементов, которые присутствуют в обоих массивах. При этом необходимо отбирать только уникальные элементы.
Сначала создаем хеш, ключами которого являются значения первого массива. Операция поиска по ключу в хеше имеет сложность O(1).
Затем перебираем элементы второго массива и проверяем, есть ли они в первом.
Общая сложность алгоритма — O(n).
В неотсортированном массиве целых чисел найти наибольшее произведение любых трех элементов
Помните, что максимальным необязательно окажется произведение трех самых больших чисел в массиве. Если минимальные элементы отрицательны, но велики по модулю, возможно искомым окажется произведение min1 * min2 * max1 (двум самых маленьких и одного самого большого).
Рекурсивный бинарный поиск
Вкратце: бинарный (двоичный) поиск делит отсортированный массив на половины. Подробнее — в Википедии.
Сбалансированность скобок
Функции и классы
Функция обратного вызова (callback) с простым примером
Коллбэк — это функция, которая передается в другую функцию как аргумент и выполняется после того, как закончатся какие-то другие операции. В примере ниже коллбэк логирует в консоль.
Область видимости (scope)
Каждая функция в JS имеет собственную область видимости (scope). Это коллекция переменных и правила доступа к ним. Если переменная объявлена внутри функции (в ее скоупе), то доступ к ней может получить только код внутри этой функции.
Области видимости могут быть вложены друг в друга, при этом все внутренние скоупы имеют доступ к переменным из внешних.
Замыкания (closures)
В C и большинстве других распространенных языков после возврата функции (оператор return или просто окончание работы) все локальные переменные становятся недоступными, так как фрейм стека уничтожается. В JavaScript они остаются в некотором смысле доступными.
Замыкание — это фрейм стека, который выделяется, когда функция начинает свое работу, и не освобождается после ее возврата (как если бы он был выделен в куче, а не в стеке!). Вы можете думать о переменной одновременно как об указателе на функцию, так и как о скрытом указателе на замыкание.
Говоря простыми словами, замыкание — это функция, созданная и возвращенная из другой функции, которая имеет доступ к родительскому скоупу.
Счетчик с помощью замыкания (closure)
Если мы создаем счетчик, то, скорее всего, не хотим, чтобы внешний код мог повлиять на накопленное значение. Значит, его нужно каким-то образом защитить. Замыкания отлично справляются с этой задачей.
Каррирование (закрепление аргумента)
Каррирование — преобразование функции от многих аргументов в набор функций, каждая из которых является функцией от одного аргумента. (с) Википедия.
Пример задачи. Напишите функцию createBase, которая будет фиксировать первое слагаемое и добавлять к нему любое число:
Решим задачу с помощью замыкания:
Вот еще одна задачка на ту же тему. Нужно написать функцию, которая будет перемножать полученные аргументы, но с необычным синтаксисом:
Тут все то же самое, просто мы не фиксируем первый (или первый и второй) множители в отдельной переменной.
Тут нужно понимать две основные концепции JS:
- Функция является объектом первого класса, то есть ее можно свободно возвращать из другой функции или сохранять в переменной как обычное значение.
- Замыкания, с помощью которых при необходимости можно зафиксировать первый параметр.
IIFE (Immediately Invoked Function Expression) и паттерн Модуль
IIFE, или немедленно выполняемое функциональное выражение, — это объявление функции с ее немедленным выполнением.
Такой синтаксис позволяет избежать загрязнения глобального пространства имен, ведь все необходимые переменные можно спрятать в замыкании.
С помощью IIFE в JavaScript реализуется паттерн проектирования Модуль — независимый от внешнего кода компонент.
Как создать по-настоящему приватный метод класса и в чем недостатки таких методов?
Создать действительно приватный метод в JS можно, только поместив его в замыкание конструктора.
Недостатком такого способа является неизбежное дублирование. Функция increaseSalary будет создаваться для каждого экземпляра Employee, хотя в этом нет необходимости.
Паттерн Прототип, прототипное наследование
Паттерн Прототип (Шаблон Свойств) создает новые объекты, которые сразу же инициализируются значениями, скопированными из некоторого образца (прототипа). Это могут быть дефолтные значения из базы данных, например.
Этот Паттерн нечасто используется в классических языках программирования, но в JavaScript на нем полностью построена объектная модель. До ES6 даже привычного синтаксиса классов не было (сейчас это просто синтаксический сахар).
Смысл прототипного наследования заключается в том, что свойство или метод объекта, к которому происходит обращение, ищется интерпретатором сначала в самом объекте, затем в его прототипе, прототипе прототипа и так далее по цепочке.
Задача по прототипной модели
Что выведет этот код?
Ответ: ‘xyz’. Оператор delete удаляет только собственные свойства объекта, а свойство company определено в прототипе.
Проверить, является ли свойство собственным можно с помощью метода emp1.hasOwnProperty(‘company’) .
Можно удалить свойство напрямую из прототипа: delete emp1.__proto__.company .
Ключевое слово new
new создает новый объект с типом object и устанавливает его внутреннее свойство [[prototype]] ( __proto__ — равно свойству prototype функции-конструктора). Указатель this при этом указывает на только что созданный объект. После того как конструктор модифицирует этот объект, он возвращается.
Выглядит это примерно так:
Ключевое слово this
this всегда ссылается на объект, в контексте которого вызван метод. Этот объект определяется динамически, его можно менять. Метод, определенный в одном объекте, вполне может быть вызван в контексте другого.
Обратите внимание, при вызове функции с new она выступает как конструктор и возвращает пустой объект, у которого не определено свойство bar .
Если вызвать функцию без контекста ( foo() ) в строгом режиме, будет ошибка, так как this в этом случае не определен.
Привязка контекста функции — метод bind
Метод bind() создает новую функцию, для которой зафиксирован this и последовательность аргументов, предшествующих аргументам при вызове новой функции.
Удобно использовать для привязки контекста или закрепления параметров.
Как добавить массивам пользовательский метод?
Так как JavaScript основан на прототипах, чтобы добавить новый метод всем массивам, нужно определить его в прототипе массивов (объект Array.prototype ).
Браузерный JS
Всплытие событий и как его остановить
Всплытие событий — это концепция DOM-модели. Когда событие генерируется на элементе, все родительские элементы последовательно о нем оповещаются. Поэтому вы можете повесить обработчик клика на контейнер карточки и таким образом узнать о клике по вложенной кнопке.
70 вопросов по JavaScript для подготовки к собеседованию
Надеюсь, эта статья будет полезна как начинающим разработчикам, так и опытным.
В вопросах, которые показались мне сложнее прочих, приведены ссылки на дополнительную литературу.
Буду признателен за развернутые комментарии. Все замечания будут учтены при редактировании статьи.
70 вопросов по JavaScript для подготовки к собеседованию
1. В чем разница между null и undefined?
Для начала давайте поговорим о том, что у них общего.
Во-первых, они принадлежат к 7 «примитивам» (примитивным типам) JS:
Во-вторых, они являются ложными значениями, т.е. результатом их преобразования в логическое значение с помощью Boolean() или оператора «!!» является false:
Ладно, теперь о различиях.
- переменной, которой не было присвоено значения, т.е. объявленной, но не инициализированной переменной;
- функции, которая ничего не возвращает явно, например, console.log(1);
- несуществующего свойства объекта.
null — это «значение отсутствия значения». null — это значение, которое присваивается переменной явно. В примере ниже мы получаем null, когда метод fs.readFile отрабатывает без ошибок:
При сравнении null и undefined мы получаем true, когда используем оператор «==», и false при использовании оператора «== javascript»>console.log(null == undefined) // true console.log(null === undefined) // false
2. Для чего используется оператор «&&»?
Оператор «&&» (логическое и) находит и возвращает первое ложное значение либо последний операнд, когда все значения истинные. Он использует короткое замыкание во избежание лишних затрат:
С оператором «if»:
То же самое с оператором «&&»:
3. Для чего используется оператор «||»?
Оператор «||» (логическое или) находит и возвращает первое истинное значение. Он также использует короткое замыкание. Данный оператор использовался для присвоения параметров по умолчанию в функциях до того, как параметры по умолчанию были стандартизированы в ES6.
4. Является ли использование унарного плюса (оператор «+») самым быстрым способом преобразования строки в число?
Согласно MDN оператор «+» действительно является самым быстрым способом преобразования строки в число, поскольку он не выполняет никаких операций со значением, которое является числом.
5. Что такое DOM?
DOM или Document Object Model (объектная модель документа) — это прикладной программный интерфейс (API) для работы с HTML и XML документами. Когда браузер первый раз читает («парсит») HTML документ, он формирует большой объект, действительно большой объект, основанный на документе — DOM. DOM представляет собой древовидную структуру (дерево документа). DOM используется для взаимодействия и изменения самой структуры DOM или его отдельных элементов и узлов.
Допустим, у нас есть такой HTML:
DOM этого HTML выглядит так:

В JS DOM представлен объектом Document. Объект Document имеет большое количество методов для работы с элементами, их созданием, модификацией, удалением и т.д.
6. Что такое распространение события (Event Propagation)?
- Фаза погружения (захвата, перехвата) — событие возникает в объекте Window и опускается до цели события через всех ее предков.
- Целевая фаза — это когда событие достигает целевого элемента.
- Фаза всплытия — событие поднимается от event.target, последовательно проходит через всех его предков и достигает объекта Window.
Подробнее о распространении событий можно почитать здесь и здесь.
7. Что такое всплытие события?
Когда событие происходит в элементе DOM, оно затрагивает не только этот элемент. Событие «всплывает» (подобно пузырьку воздуха в воде), переходит от элемента, вызвавшего событие (event.target), к его родителю, затем поднимается еще выше, к родителю родителя элемента, пока не достигает объекта Window.
Допустим, у нас есть такая разметка:
У метода addEventListener есть третий необязательный параметр — useCapture. Когда его значение равняется false (по умолчанию), событие начинается с фазы всплытия. Когда его значение равняется true, событие начинается с фазы погружения (для «прослушивателей» событий, прикрепленных к цели события, событие находится в целевой фазе, а не в фазах погружения или всплытия. События в целевой фазе инициируют все прослушиватели на элементе в том порядке, в котором они были зарегистрированы независимо от параметра useCapture — прим. пер.). Если мы кликнем по элементу child, в консоль будет выведено: child, parent, grandparent, html, document, window. Вот что такое всплытие события.
8. Что такое погружение события?
Когда событие происходит в элементе DOM, оно происходит не только в нем. В фазе погружения событие опускается от объекта Window до цели события через всех его предков.
У метода addEventListener есть третий необязательный параметр — useCapture. Когда его значение равняется false (по умолчанию), событие начинается с фазы всплытия. Когда его значение равняется true, событие начинается с фазы погружения. Если мы кликнем по элементу child, то увидим в консоли следующее: window, document, html, grandparent, parent, child. Это и есть погружение события.
9. В чем разница между методами event.preventDefault() и event.stopPropagation()?
Метод event.preventDefault() отключает поведение элемента по умолчанию. Если использовать этот метод в элементе form, то он предотвратит отправку формы (submit). Если использовать его в contextmenu, то контекстное меню будет отключено (данный метод часто используется в keydown для переопределения клавиатуры, например, при создании музыкального/видео плеера или текстового редактора — прим. пер.). Метод event.stopPropagation() отключает распространение события (его всплытие или погружение).
10. Как узнать об использовании метода event.preventDefault()?
Для этого мы можем использовать свойство event.defaulPrevented, возвращающее логическое значение, служащее индикатором применения к элементу метода event.preventDefault.
11. Почему obj.someprop.x приводит к ошибке?
Ответ очевиден: мы пытается получить доступ к свойству x свойства someprop, которое имеет значение undefined. obj.__proto__.__proto = null, поэтому возвращается undefined, а у undefined нет свойства x.
12. Что такое цель события или целевой элемент (event.target)?
Простыми словами, event.target — это элемент, в котором происходит событие, или элемент, вызвавший событие.
Имеем такую разметку:
И такой простенький JS:
Мы прикрепили «слушатель» к внешнему div. Однако если мы нажмем на кнопку, то получим в консоли разметку этой кнопки. Это позволяет сделать вывод, что элементом, вызвавшим событие, является именно кнопка, а не внешний или внутренние div.
13. Что такое текущая цель события (event.currentTarget)?
Event.currentTarget — это элемент, к которому прикреплен прослушиватель событий.
И немного видоизмененный JS:
Мы прикрепили слушатель к внешнему div. Куда бы мы ни кликнули, будь то кнопка или один из внутренних div, в консоли мы всегда получим разметку внешнего div. Это позволяет заключить, что event.currentTarget — это элемент, к которому прикреплен прослушиватель событий.
14. В чем разница между операторами «==» и «== ==» (абстрактное или нестрогое равенство) и оператором «== ==» производит так называемое неявное сравнение. Оператор «= == https://habrastorage.org/r/w1560/webt/yd/xe/tn/ydxetnfghjtfuex_p-em8v4emck.png» data-src=»https://habrastorage.org/webt/yd/xe/tn/ydxetnfghjtfuex_p-em8v4emck.png»/>
Все примеры возвращают true.
Первый пример — первое условие алгоритма.
Второй пример — четвертое условие.
Третий — второе.
Четвертый — седьмое.
Пятый — восьмое.
И последний — десятое.

15. Почему результатом сравнения двух похожих объектов является false?
В JS объекты и примитивы сравниваются по-разному. Примитивы сравниваются по значению. Объекты — по ссылке или адресу в памяти, где хранится переменная. Вот почему первый console.log возвращает false, а второй — true. Переменные «a» и «c» ссылаются на один объект, а переменные «a» и «b» — на разные объекты с одинаковыми свойствами и значениями.
16. Для чего используется оператор «!!»?
Оператор «!!» (двойное отрицание) приводит значение справа от него к логическому значению.
17. Как записать несколько выражений в одну строку?
Для этого мы можем использовать оператор «,» (запятая). Этот оператор «двигается» слева направо и возвращает значение последнего выражения или операнда.
Если мы выведем значение x в консоль, то получим 27. Сначала мы увеличиваем значение x на единицу (x = 6). Затем вызываем функцию addFive() с параметром 6, к которому прибавляем 5 (x = 11). После этого мы умножаем значение x на 2 (x = 22). Затем вычитаем 5 (x = 17). И, наконец, прибавляем 10 (x = 27).
18. Что такое поднятие (Hoisting)?
Поднятие — это термин, описывающий подъем переменной или функции в глобальную или функциональную области видимости.
Для того, чтобы понять, что такое Hoisting, необходимо разобраться с тем, что представляет собой контекст выполнения.
Контекст выполнения — это среда, в которой выполняется код. Контекст выполнения имеет две фазы — компиляция и собственно выполнение.
Компиляция. В этой фазе функциональные выражения и переменные, объявленные с помощью ключевого слова «var», со значением undefined поднимаются в самый верх глобальной (или функциональной) области видимости (как бы перемещаются в начало нашего кода. Это объясняет, почему мы можем вызывать функции до их объявления — прим. пер.).
Выполнение. В этой фазе переменным присваиваются значения, а функции (или методы объектов) вызываются или выполняются.
Запомните: поднимаются только функциональные выражения и переменные, объявленные с помощью ключевого слова «var». Обычные функции и стрелочные функции, а также переменные, объявленные с помощью ключевых слов «let» и «const» не поднимаются.
Предположим, что у нас есть такой код:
Получаем undefined, 1 и ‘Hello Mark!’.
Вот как выглядит фаза компиляции:
После завершения фазы компиляции начинается фаза выполнения, когда переменным присваиваются значения и вызываются функции.
Дополнительно о Hoisting можно почитать здесь.
19. Что такое область видимости (Scope)?
Область видимости — это место, где (или откуда) мы имеем доступ к переменным или функциям. JS имеем три типа областей видимости: глобальная, функциональная и блочная (ES6).
Глобальная область видимости — переменные и функции, объявленные в глобальном пространстве имен, имеют глобальную область видимости и доступны из любого места в коде.
Функциональная область видимости (область видимости функции) — переменные, функции и параметры, объявленные внутри функции, доступны только внутри этой функции.
Блочная область видимости — переменные (объявленные с помощью ключевых слов «let» и «const») внутри блока (< >), доступны только внутри него.
Область видимости — это также набор правил, по которым осуществляется поиск переменной. Если переменной не существует в текущей области видимости, ее поиск производится выше, во внешней по отношению к текущей области видимости. Если и во внешней области видимости переменная отсутствует, ее поиск продолжается вплоть до глобальной области видимости. Если в глобальной области видимости переменная обнаружена, поиск прекращается, если нет — выбрасывается исключение. Поиск осуществляется по ближайшим к текущей областям видимости и останавливается с нахождением переменной. Это называется цепочкой областей видимости (Scope Chain).

20. Что такое замыкание (Closures)?
Наверное, это самый сложный вопрос из списка. Я постараюсь объяснить, как я понимаю замыкание.
По сути, замыкание — это способность функции во время создания запоминать ссылки на переменные и параметры, находящиеся в текущей области видимости, в области видимости родительской функции, в области видимости родителя родительской функции и так до глобальной области видимости с помощью цепочки областей видимости. Обычно область видимости определяется при создании функции.
Примеры — отличный способ объяснить замыкание:
В данном примере, когда мы объявляем функцию, глобальная область видимости является частью замыкания.

Переменная «globalVar» не имеет значения на картинке, потому что ее значение может меняться в зависимости от того, где и когда будет вызвана функция. Но в примере выше globalVar будет иметь значение «abc».
Теперь пример посложнее:

В результате получаем «guess outer inner». Объяснение следующее: когда мы вызываем функцию outerFunc и присваиваем переменной «x» значение, возвращаемое функцией innerFunc, параметр «outerParam» равняется «outer». Несмотря на то, что мы присвоили переменной «outerVar» значение «outer-2», это произошло после вызова функции outerFunc, которая «успела» найти значение переменной «outerVar» в цепочке областей видимости, этим значением было «outer». Когда мы вызываем «x», которая ссылается на innerFunc, значением «innerParam» является «inner», потому что мы передаем это значение в качестве параметра при вызове «x». globalVar имеет значение «guess», потому что мы присвоили ей это значение перед вызовом «x».
Пример неправильного понимания замыкания.
Данный код работает не так, как ожидается. Объявление переменной с помощью ключевого слова «var» делает эту переменную глобальной. После добавления функций в массив «arrFunc» значением глобальной переменной «i» становится «5». Поэтому когда мы вызываем функцию, она возвращает значение глобальной переменной «i». Замыкание хранит ссылку на переменную, а не на ее значение во время создания. Эту проблему можно решить, используя IIFE или объявив переменную с помощью ключевого слова «let».
Подробнее о замыкании можно почитать здесь и здесь.
21. Какие значения в JS являются ложными?
Ложными являются значения, результатом преобразования которых в логическое значение является false.
22. Как проверить, является ли значение ложным?
Следует использовать функцию Boolean или оператор «!!» (двойное отрицание).
23. Для чего используется директива «use strict»?
«use strict» — это директива ES5, которая заставляет весь наш код или код отдельной функции выполняться в строгом режиме. Строгий режим вводит некоторые ограничения по написанию кода, тем самым позволяя избегать ошибок на ранних этапах.
Вот какие ограничения накладывает строгий режим.
Нельзя присваивать значения или обращаться к необъявленным переменным:
Запрещено присваивать значения глобальный переменным, доступным только для чтения или записи:
Нельзя удалить «неудаляемое» свойство объекта:
Запрещено дублирование параметров:
Нельзя создавать функции с помощью функции eval:
Значением «this» по умолчанию является undefined:
24. Какое значение имеет this?
Обычно this ссылается на значение объекта, который в данный момент выполняет или вызывает функцию. «В данный момент» означает, что значение this меняется в зависимости от контекста выполнения, от того места, где мы используем this.
В данном случае метод getName возвращает this.name, а this ссылается на carDetails, объект, в котором выполняется getName, который является ее «владельцем».
Добавим после console.log три строчки:
Второй console.log выдает Ford Ranger, и это странно. Причина такого поведения заключается в том, что «владельцем» getCarName является объект window. Переменные, объявленные с помощью ключевого слова «var» в глобальной области видимости, записываются в свойства объекта window. this в глобальной области видимости ссылается на объект window (если речь не идет о строгом режиме).
В этом примере this и window ссылаются на один объект.
Одним из способов решения данной проблемы является использование методов call или apply:
Call и apply принимают в качестве первого аргумента объект, который будет являться значением this внутри функции.
В IIFE, функциях, которые создаются в глобальном области видимости, анонимных функциях и внутренних функциях методов объекта значением this по умолчанию является объект window.
Существует два способа получить «Marko Polo».
Во-первых, мы можем сохранить значение this в переменной:
Во-вторых, мы можем использовать стрелочную функцию:
Стрелочные функции не имеют собственного значения this. Они копируют значение this из внешнего лексического окружения.
25. Что такое прототип объекта?
В двух словах, прототип — это план (схема или проект) объекта. Он используется как запасной вариант для свойств и методов, существующих в данном объекте. Это также один из способов обмена свойствами и функциональностью между объектами. Это основная концепция прототипного наследования в JS.
Несмотря на то, что объект «о» не имеет свойства toString, обращение к этому свойству не вызывает ошибки. Если определенного свойства нет в объекте, его поиск осуществляется сначала в прототипе объекта, затем в прототипе прототипа объекта и так до тех пор, пока свойство не будет найдено. Это называется цепочкой прототипов. На вершине цепочки прототипов находится Object.prototype.
Подробнее о прототипах и наследовании можно почитать здесь и здесь.
26. Что такое IIFE?
IIFE или Immediately Invoked Function Expression — это функция, которая вызывается или выполняется сразу же после создания или объявления. Для создания IIFE необходимо обернуть функцию в круглые скобки (оператор группировки), превратив ее в выражение, и затем вызвать ее с помощью еще одних круглых скобок. Это выглядит так: (function()<>)().
Все эти примеры являются валидными. Предпоследний пример показывает, что мы можем передавать параметры в IIFE. Последний пример показывает, что мы можем сохранить результат IIFE в переменной.
Лучшее использование IIFE — это выполнение функций настройки инициализации и предотвращение конфликтов имен с другими переменными в глобальной области видимости (загрязнение глобального пространства имен). Приведем пример.
У нас есть ссылка на библиотеку somelibrary.js, которая предоставляет некоторые глобальные функции, которые мы можем использовать в нашем коде, но в этой библиотеке есть два метода, createGraph и drawGraph, которые мы не используем, потому что они содержат ошибки. И мы хотим реализовать эти функции самостоятельно.
Одним из способов решить данную проблему является изменение структуры наших скриптов:
Таким образом, мы переопределяем методы, предоставляемые библиотекой.
Вторым способом является изменение имен наших функций:
Третий способ — использование IIFE:
В этом примере мы создаем служебную переменную, которая содержит результат IIFE, возвращающий объект, содержащий методы createGraph и drawGraph.
Вот еще одна проблема, которую можно решить с помощью IIFE:
Допустим, у нас есть элемент «ul» с классом «list-group», содержащий 5 дочерних элементов «li». И мы хотим выводить в консоль значение «i» при клике по отдельному «li». Однако вместо этого в консоль всегда выводится 5. Виной всему замыкание.
Одним из решений является IIFE:
Причина, по которой этот код работает, как задумано, состоит в том, что IIFE создает новую область видимости на каждой итерации, и мы записываем значение «i» в currentIndex.
27. Для чего используется метод Function.prototype.apply?
Apply используется для привязки определенного объекта к значению this вызываемой функции.
Этот метод похож на Function.prototype.call. Единственное отличие состоит в том, что в apply аргументы передаются в виде массива.
28. Для чего используется метод Function.prototype.call?
Call используется для привязки определенного объекта к значению this вызываемой функции.
Этот метод похож на Function.prototype.apply. Отличие состоит в том, что в call аргументы передаются через запятую.
29. В чем разница между методами call и apply?
Отличие между call и apply состоит в том, как мы передаем аргументы в вызываемой функции. В apply аргументы передаются в виде массива, в call — через запятую.
30. Для чего используется метод Function.prototype.bind?
Bind возвращает новую функцию, значением this которой является объект, указанный в качестве первого параметра. В отличие от bind, call и apply сразу же вызывают функцию.
31. Что такое функциональное программирование и какие особенности JS позволяют говорить о нем как о функциональном языке программирования?
Функциональное программирование — это декларативная концепция программирования или образец (паттерн) того, как строятся приложения, как используются функции, содержащие выражения, которые вычисляют значения без изменения аргументов, которые им передаются.
Объект Array содержит методы map, filter и reduce, которые являются самыми известными функциями в мире функционального программирования из-за их полезности, а также потому, что они не изменяют массив, что делает эти функции «чистыми». Также в JS имеются замыкание и функции высшего порядка, которые являются характеристиками функционального языка программирования.
Метод map возвращает новый массив с результатами вызова callback для каждого элемента массива:
Метод filter создает новый массив со всеми элементами, которые удовлетворяют условию, указанному в callback:
Метод reduce выполняет callback один раз для каждого элемента массива, за исключением пустот, принимая четыре аргумента: начальное значение (или значение от предыдущего callback), значение текущего элемента, текущий индекс и итерируемый массив:
32. Что такое функции высшего порядка (Higher Order Functions)?
Функция высшего порядка — это функция, возвращающая другую функцию или принимающая другую функцию в качестве аргумента.
33. Почему функции в JS называют объектами первого класса (First-class Objects)?
Функции называют объектами первого класса, потому что они обрабатываются также, как и любое другое значение в JS. Они могут присваиваться переменным, быть свойством объекта (методом), элементом массива, аргументом другой функции, значением, возвращаемым функцией. Единственным отличием функции от любого другого значения в JS является то, что функция может быть выполнена или вызвана.
34. Как бы Вы реализовали метод Array.prototype.map?
Метод map создает новый массив с результатом вызова указанной функции для каждого элемента массива.
35. Как бы Вы реализовали метод Array.prototype.filter?
Метод filter создает новый массив со всеми элементами, прошедшими проверку, задаваемую в передаваемой функции.
36. Как бы Вы реализовали метод Array.prototype.reduce?
Метод reduce применяет функцию reducer к каждому элементу массива (слева-направо), возвращая одно результирующее значение.
37. Что такое объект arguments?
Arguments — это коллекция аргументов, передаваемых функции. Это объект, подобный массиву, у него есть свойство length, мы можем получить доступ к определенному значению с помощью arguments[i], но у него отсутствуют методы forEach, reduce, filter и map. Он позволяет узнать количество параметров функции.
Преобразовать arguments в массив можно с помощью Array.prototype.slice:
Запомните: в стрелочных функциях объект arguments не работает.
Вызов функции four приводит к ошибке ReferenceError: arguments is not defined. Эту проблему можно решить с помощью оператора rest:
Это автоматически поместит все параметры в массив.
38. Как создать объект, не имеющий прототипа?
Это можно сделать с помощью Object.create:
39. Почему в представленном коде переменная b становится глобальной при вызове функции?
Так происходит, потому что оператор присваивания (» javascript»>function myFunc() < let a = (b = 0) >myFunc()
Сначала значение 0 присваивается переменной «b», которая не объявлена. Движок JS делает ее глобальной. Возвращаемое выражением b = 0 значение (0) затем присваивается локальной переменной «a».
Эту проблему можно решить сначала объявив локальные переменные, а затем присвоив им значения:
40. Что такое ECMAScript?
ECMAScript — это спецификация, стандарт скриптовых языков программирования, он является основой JS, поэтому любые изменения ECMAScript отражаются на JS.
Последний вариант спецификации ECMA-262 можно посмотреть здесь.
41. Что нового привнес в JS стандарт ES6 или ECMAScript2015?
- Стрелочные функции (Arrow Functions).
- Классы (Classes).
- Шаблонные строки (Template Strings).
- Расширенные объектные литералы (Enhanced Object literals).
- Деструктуризация (Object Destructuring).
- Промисы (Promises).
- Генераторы (Generators).
- Модули (Modules).
- Symbol.
- Прокси (Proxies).
- Множества (Sets).
- Параметры по умолчанию.
- Операторы rest и spread.
- Блочная область видимости (ключевые слова «let» и «const»).
42. В чем разница между ключевыми словами «var», «let» и «const»?
Переменные, объявленные с помощью ключевого слова «var», являются глобальными. Это означает, что они доступны из любого места в коде:
Результатом первого console.log будет undefined, второго — 5. Мы имеем доступ к переменной «x» из-за ее всплытия в глобальную область видимости. Код из примера выше интерпретируется следующим образом:
Результатом первого console.log является undefined, поскольку объявленные переменные, которым не присвоено значения, имеют значение undefined по умолчанию.
Переменные, объявленные с помощью ключевых слов «let» и «const» имеют блочную область видимости. Это означает, что они доступны только внутри блока (< >):
Вызов этих функций с параметром false приведет к ошибке ReferenceError, потому что к переменным «x» и «y» нет доступа снаружи блока и их значения не возвращаются (не всплывают).
Разница между «let» и «const» состоит в том, что в первом случае мы может менять значение переменной, а во втором — нет (константа). При этом, мы можем менять значение свойства объекта, объявленного с помощью const, но не само свойство (переменную).
43. Что такое стрелочные функции (Arrow Functions)?
Стрелочная функция — это относительно новый способ создания функций в JS. Стрелочные функции создаются быстрее и имеют более читаемый синтаксис, чем функциональные выражения. В стрелочных функциях опускается слово «function»:
В функциональном выражении мы используем ключевое слово «return» для возврата значения. В стрелочной функции мы этого не делаем, поскольку стрелочные функции неявно возвращают значения при условии, что мы возвращаем одно выражение или значение:
Мы также можем передавать параметры стрелочным функциям. Если мы передаем один параметр, его можно не оборачивать в круглые скобки:
У стрелочных функций нет доступа к объекту arguments. Поэтому вызов первой функции приведет к ошибке. Для получения параметров, переданных функции, мы можем использовать оператор rest.
44. Что такое классы (Classes)?
Классы — это относительно новый способ написания функций-конструкторов в JS. Это синтаксический сахар для функций-конструкторов. В основе классов лежат те же прототипы и прототипное наследование:
Переопределение методов и наследование от другого класса:
Как узнать об использовании прототипов?
45. Что такое шаблонные литералы (Template Literals)?
Шаблонные литералы — относительно новый способ создания строк в JS. Шаблонные литералы создаются с помощью двойных обратных кавычек («):
В шаблонных литералах нам не нужно экранировать одинарные кавычки.
В ES6 нам не нужно использовать управляющую последовательность «\n» для перевода строки.
В ES6 нам не нужно использовать конкатенацию строк для объединения текста с переменной: мы можем использовать выражение $
46. Что такое деструктуризация объекта (Object Destructuring)?
Деструктуризация — относительно новый способ получения (извлечения) значений объекта или массива.
Допустим, у нас есть такой объект:
Раньше для получения свойств объекта мы создавали переменные для каждого свойства. Это было очень скучно и сильно раздражало:
Использование деструктуризации позволяет сделать код чище и отнимает меньше времени. Синтаксис деструктуризации следующий: заключаем свойства объекта, которые хотим получить, в фигурные скобки (< >), а если речь идет о массиве — в квадратные скобки ([ ]):
Для изменения имени переменной следует использовать «propertyName: newName»:
Для присвоения переменным значения по умолчанию следует использовать «propertyName = ‘defaultValue’»:
47. Что такое модули (Modules)?
Модули позволяют объединять (использовать) код из разных файлов и избавляют нас от необходимости держать весь код в одном большом файле. До появления модулей в JS существовало две популярные системы модулей для поддержки кода:
- CommonJS — Nodejs
- AMD (AsyncronousModuleDefinition) — Browsers
Экспорт функциональности в другой файл (именной экспорт):
Импорт функциональности в другой файл:
Экспорт по умолчанию:
Это базовое использование модулей. Я не стал вдаваться в подробности, поскольку мой пост и без того получается слишком большим.
48. Что такое объект Set?
Объект Set позволяет хранить уникальные значения, примитивы и ссылки на объекты. Еще раз: в Set можно добавлять только уникальные значения. Он проверяет хранящиеся в нем значения с помощью алгоритма SameZeroValue.
Экземпляр Set создается с помощью конструктора Set. Мы также можем передать ему некоторые значения при создании:
Мы можем добавлять значения в Set, используя метод add. Поскольку метод add является возвращаемым, мы может использовать цепочку вызовов:
Мы можем удалять значения из Set, используя метод delete:
Мы можем проверить наличие свойства в Set, используя метод has:
Для получения длины Set используется метод size:
Метод clear очищает Set:
Мы можем использовать Set для удаления повторяющихся значений в массиве:
49. Что такое функция обратного вызова (Callback Function)?
Функция обратного вызова — это функция, вызов которой отложен на будущее (происходит при некоторых условиях, например, при наступлении события).
В примере мы ждем события «клик» на элементе с идентификатором «btnAdd». По клику вызывается функция clickCallback. Функция обратного вызова добавляет некоторый функционал данным или событию. Методам reduce, filter и map в качестве второго аргумента передается функция обратного вызова. Хорошей аналогией callback является следующая ситуация: Вы звоните кому-то, он не отвечает, Вы оставляете ему сообщение и ждете, когда он перезвонит. Звонок или сообщение — это событие или данные, а callback — это ожидание (предвосхищение) встречного звонка.
50. Что такое промисы (Promises)?
Промисы — это один из приемов работы с асинхронным кодом в JS. Они возвращают результат асинхронной операции. Промисы были придуманы для решения проблемы так называемого «ада функций обратного вызова».
Проблемы при таком подходе начинаются, когда нам необходимо добавить еще одну асинхронную операцию в первую (внутрь первой), затем еще одну и т.д. В результате мы получаем беспорядочный и нечитаемый код:
А вот как это выглядит с промисами:
У промиса есть четыре состояния:
- Ожидание — начальное состояние промиса. Результата промиса неизвестен, поскольку операция не завершена.
- Выполнено — асинхронная операция выполнена, имеется результат.
- Отклонено — асинхронная операция не выполнена, имеется причина.
- Завершено — выполнено или отклонено.
Мы можем создать вспомогательную функцию для преобразования асинхронной операции с callback в промис. Она будет работать наподобие util из Node.js («промисификация»):
Подробнее о промисах можно почитать здесь и здесь.
51. Что такое async/await?
Async/await — относительно новый способ написания асинхронного (неблокирующего) кода в JS. Им оборачивают промис. Он делает код более читаемым и чистым, чем промисы и функции обратного вызова. Однако для использования async/await необходимо хорошо знать промисы.
Запомните: использование ключевого слова «async» перед функцией заставляет ее возвращать промис:
Ключевое слово «await» можно использовать только внутри асинхронной функции. Использование «await» внутри другой функции приведет к ошибке. Await ожидает завершения выражения справа, чтобы вернуть его значение перед выполнением следующей строчки кода.
Подробнее об async/await можно почитать здесь и здесь.
52. В чем разница между spread-оператором и rest-оператором?
Операторы spread и rest имеют одинаковый синтаксис («. «). Разница состоит в том, что с помощью spread мы передаем или распространяем данные массива на другие данные, а с помощью rest — получаем все параметры функции и помещаем их в массив (или извлекаем часть параметров).
В этом примере мы используем spread при вызове функции add с данными массива nums. Значением переменной «a» будет 5, b = 6, sum = 11.
Здесь мы вызываем функцию add с любым количеством аргументов. Add возвращает сумму этих аргументов.
В этом примере мы используем rest для помещения любого количества параметров, кроме первого, в массив others.
53. Что такое параметры по умолчанию (Default Parameters)?
Это относительно новый способ определения значений переменных по умолчанию.
Можно использовать деструктуризацию:
Мы даже можем использовать по умолчанию параметры, объявленные в том же месте:
54. Что такое объектная обертка (Wrapper Objects)?
Примитивы строка, число и boolean имеют свойства и методы, несмотря на то, что они не являются объектами:
Name — это строка (примитивный тип), у которого нет свойств и методов, но когда мы вызываем метод toUpperCase(), это приводит не к ошибке, а к «MARKO».
Причина такого поведения заключается в том, что name временно преобразуется в объект. У каждого примитива, кроме null и undefined, есть объект-обертка. Такими объектами являются String, Number, Boolean, Symbol и BigInt. В нашем случае код принимает следующий вид:
Временный объект отбрасывается по завершении работы со свойством или методом.
55. В чем разница между явным и неявным преобразованием или приведением к типу (Implicit and Explicit Coercion)?
Неявное преобразование — это способ приведения значения к другому типу без нашего ведома (участия).
Предположим, у нас есть следующее:
Результатом первого console.log будет 16. В других языках это привело бы к ошибке, но в JS 1 конвертируется в строку и конкатенируется (присоединяется) c 6. Мы ничего не делали, преобразование произошло автоматически.
Результатом второго console.log будет 1. False было преобразовано в 0, true — в 1. 0 + 1 = 1.
Результатом третьего console.log будет 12. Строка 2 была преобразована в число перед умножением на 6.
Явное преобразование предполагает наше участие в приведении значения к другому типу:
В этом примере мы используем parseInt для приведения строки 6 к числу, затем складываем два числа и получаем 7.
56. Что такое NaN? Как проверить, является ли значение NaN?
NaN или Not A Number (не число) — это значение, получаемое в результате выполнения числовой операции над нечисловым значением:
В JS есть встроенный метод isNaN, позволяющий проверять, является ли значение NaN, но он ведет себя довольно странно:
Результатом всех console.log является true, несмотря на то, что ни одно из значений не является NaN.
ES6 для проверки, является ли значение NaN, рекомендует использовать метод Number.isNaN. Мы также можем написать вспомогательную функцию для решения проблемы «неравенства NaN самому себе»:
57. Как проверить, является ли значение массивом?
Для этого следует использовать метод Array.isArray:
Если среда, в которой Вы работаете, не поддерживает данный метод, можете использовать такой полифил:
58. Как проверить, что число является четным, без использования деления по модулю или деления с остатком (оператора «%»)?
Для решения данной задачи можно использовать оператор «&» (бинарное и). Оператор «&» сравнивает операнды как бинарные значения.
0 в бинарной системе счисления это 000
1 — это 001
2 — 010
3 — 011
4 — 100
5 — 101
6 — 110
7 — 111
и т.д.

Console.log(5 & 1) вернет 1. Сначала оператор «&» конвертирует оба числа в бинарные значения, 5 превращается в 101, 1 — в 001. Затем производится побитовое сравнение:

Сравниваем 1 и 0, получаем 0.
Сравниваем 0 и 0, получаем 0.
Сравниваем 1 и 1, получаем 1.
Преобразуем бинарное значение в целое число, получаем 1.
Если эта информация кажется Вам слишком сложной, мы можем решить поставленную задачу с помощью рекурсивной функции:
59. Как определить наличие свойства в объекте?
Существует три способа это сделать.
Первый способ состоит в использовании оператора «in»:
Второй — использовать метод hasOwnProperty:
Третий — индексная нотация массива:
60. Что такое AJAX?
AJAX или Asyncronous JavaScript and XML — это набор взаимосвязанных технологий, которые позволяют работать с данными в асинхронном режиме. Это означает, что мы можем отправлять данные на сервер и получать данные с него без перезагрузки веб-страницы.
AJAX использует следующие технологии:
HTML — структура веб-страницы.
CSS — стили веб-страницы.
JavaScript — поведение страницы и работа с DOM.
XMLHttpRequest API — отправка и получение данных с сервера.
PHP, Python, Nodejs — какой-нибудь серверный язык.
61. Как в JS создать объект?
62. В чем разница между методами Object.freeze и Object.seal?
Разница заключается в том, что при использовании метода Object.freeze мы не можем менять или редактировать свойства объекта, а при использовании Object.seal у нас такая возможность имеется.
63. В чем разница между оператором «in» и методом hasOwnProperty?
Отличие состоит в том, что оператор «in» проверяет наличие свойства не только в самом объекте, но и в его прототипах, а метод hasOwnProperty — только в объекте.
64. Какие приемы работы с асинхронным кодом в JS Вы знаете?
- Функции обратного вызова (Callbacks).
- Промисы (Promises).
- Async/await.
- Библиотеки вроде async.js, blueprint, q, co.
65. В чем разница между обычной функцией и функциональным выражением?
Допустим, у нас есть следующее:
Вызов notHoistedFunc приведет к ошибке, а вызов hoistedFunc нет, потому что hoistedFunc «всплывает», поднимается в глобальную область видимости, а notHoistedFunc нет.
66. Как в JS вызвать функцию?
В JS существует 4 способа вызвать функцию. Вызов определяет значение this или «владельца» функции.
Вызов в качестве функции. Если функция вызывается как метод, конструктор или с помощью методов apply или call, значит она вызывается как функция. Владельцем такой функции является объект window:
Вызов в качестве метода. Когда функция является свойством объекта, мы называем ее методом. Когда вызывается метод, значением this становится объект этого метода:
Вызов в качестве конструктора. Когда функция вызывается с использованием ключевого слова «new», мы называем такую функцию конструктором. При этом создается пустой объект, являющийся значением this:
Вызов с помощью методов apply или call. Мы используем эти методы, когда хотим явно определить значение this или владельца функции:
67. Что такое запоминание или мемоизация (Memoization)?
Мемоизация — это прием создания функции, способной запоминать ранее вычисленные результаты или значения. Преимущество мемоизации заключается в том, что мы избегаем повторного выполнения функции с одинаковыми аргументами. Недостатком является то, что мы вынуждены выделять дополнительную память для сохранения результатов.
68. Как бы Вы реализовали вспомогательную функцию запоминания?
Мы реализовали функцию мемоизации с одним аргументом. Сделаем ее «мультиаргументной»:
69. Почему typeof null возвращает object? Как проверить, является ли значение null?
typeof null == ‘object’ всегда будет возвращать true по историческим причинам. Поступало предложение исправить эту ошибку, изменив typeof null = ‘object’ на typeof null = ‘null’, но оно было отклонено в интересах сохранения обратной совместимости (такое изменение повлекло бы за собой большое количество ошибок).
Для проверки, является ли значение null можно использовать оператор строгого равенства (===):
70. Для чего используется ключевое слово «new»?
Ключевое слово «new» используется в функциях-конструкторах для создания нового объекта (нового экземпляра класса).
О ключевом слове «this» языка JavaScript: особенности использования с пояснениями
Долгое время ключевое слово this оставалось для меня загадкой. Это мощный инструмент, но разобраться в нём нелегко.
С точки зрения Java, PHP или любого другого обычного языка this расценивается как экземпляр текущего объекта в методе класса, не больше и не меньше. Чаще всего его нельзя использовать вне метода, и этот подход не вызывает непонимания.
В JavaScript this — это текущий контекст исполнения функции. Поскольку функцию можно вызвать четырьмя способами:
- вызов функции: alert(‘Hello World!’) ,
- вызов метода: console.log(‘Hello World!’) ,
- вызов конструктора: new RegExp(‘\\d’) ,
- непрямой вызов: alert.call(undefined, ‘Hello World!’) ,
и каждый из них определяет свой контекст, поведение this слегка не соответствует ожиданиям начинающих разработчиков. Кроме того, strict mode также влияет на контекст исполнения.
Ключом к пониманию ключевого слова this является осознание принципов вызова функции и его влияния на контекст. В этой статье рассказывается про вызовы функций, влияние вызовов на this и типичные ловушки при идентификации контекста.
Прежде чем мы начнём, давайте познакомимся с несколькими терминами:
- Вызов — это исполнение кода тела функции. Например, вызовом функции parseInt будет parseInt(’15’) .
- Контекстом вызова является значение this в теле функции.
- Область видимости функции — это набор переменных, объектов и функций, к которым можно получить доступ из тела функции.
Содержание:
Вызов функции
Вызов функции совершается, когда за выражением, являющимся объектом функции, следуют открывающая скобка ( , разделённый запятыми список аргументов и закрывающая скобка ) , например, parseInt(’18’) . Выражение не может быть аксессором myObject.myFunction , который совершает вызов метода. Например, [1,5].join(‘,’) — это вызов не функции, а метода.
Простой пример вызова функции:
hello(‘World’) — это вызов функции: hello расценивается как объект функции, за которым в скобках следует аргумент ‘World’ .
Это тоже вызов функции: первая пара скобок (function(name) <. >) расценивается как объект функции, за которым в скобках следует аргумент: (‘World’) .
this при вызове функции
this — это глобальный объект при вызове функции
Глобальный объект определяется средой исполнения. В веб-браузере это объект window .
В вызове функции контекстом исполнения является глобальный объект. Давайте проверим контекст следующей функции:
Когда вызывается sum(15, 16) , JavaScript автоматически инициализирует this как глобальный объект, являющийся window в браузере.
Когда this используется вне области видимости какой-либо функции (самая внешняя область видимости: контекст глобального исполнения), он также относится к глобальному объекту:
this при вызове функции в strict mode
this принимает значение undefined при вызове функции в strict mode
Strict mode был введён в ECMAScript 5.1 и представляет собой более надёжную систему защиты и проверки ошибок. Для активации поместите директиву ‘use strict’ вверху тела функции. Этот режим влияет на контекст исполнения, заставляя this быть undefined . Контекст исполнения перестаёт быть глобальным объектом, в отличие от предыдущего случая.
Пример функции, запущенной в strict mode:
Когда multiply(2, 5) вызывается this становится undefined .
Strict mode активен не только в текущей области видимости, но и во всех вложенных:
‘use strict’ вставлена вверху тела execute , что активирует strict mode внутри её области видимости. Поскольку concat объявлена внутри области видимости execute , она наследует strict mode. И вызов concat(‘Hello’, ‘ World!’) приводит к тому, что this становится undefined .
Один файл JavaScript может содержать как «строгие», так и «нестрогие» функции. Поэтому возможно иметь в одном скрипте разные контексты исполнения для одного типа вызова:
Ловушка: this во внутренней функции
Обычной ошибкой при работе с вызовом функции является уверенность в том, что this во внутренней функции такой же, как и во внешней.
Вообще-то контекст внутренней функции зависит только от вызова, а не от контекста внешней функции.
Чтобы получить ожидаемый this , модифицируйте контекст внутренней функции при помощи непрямого вызова (используя .call() или .apply() , об этом позже) или создайте связанную функцию (используя .bind() , об этом тоже поговорим позже).
Следующий пример вычисляет сумму двух чисел:
numbers.sum() — это вызов метода объекта, поэтому контекстом sum является объект numbers . Функция calculate определена внутри sum , поэтому вы можете ожидать, что this — это объект numbers и в calculate() . Тем не менее, calculate() — это вызов функции, а не метода, и поэтому его this — это глобальный объект window или undefined в strict mode. Даже если контекстом внешней функции sum является объект numbers , у него здесь нет власти.
Результатом вызова numbers.sum() является NaN или ошибка TypeError: Cannot read property ‘numberA’ of undefined в strict mode. Точно не ожидаемый результат 5 + 10 = 15 , а всё потому, что calculate вызвана некорректно.
Для решения проблемы функция calculate должна быть исполнена в том же контексте, что и метод sum , чтобы получить доступ к значениям numberA и numberB . Это можно сделать при помощи метода .call() :
calculate.call(this) исполняет функцию calculate , но дополнительно модифицирует контекст в соответствии с первым параметром. Теперь this.numberA + this.numberB эквивалентно numbers.numberA + numbers.numberB и функция возвращает ожидаемый результат 5 + 10 = 15 .
Вызов метода
Метод — это функция, хранящаяся в объекте. Пример:
helloFunction — это метод в myObject . Для доступа к методу нужно использовать аксессор: myObject.helloFunction .
Вызов метода совершается, когда за выражением в виде аксессора, расценивающемся как объект функции, следует пара скобок и разделенный запятыми список аргументов между ними.
В прошлом примере myObject.helloFunction() — это вызов метода helloFunction объекта myObject . Также вызовами метода являются: [1, 2].join(‘,’) или /\s/.test(‘beautiful world’) .
Важно отличать вызов функции от вызова метода. Главным отличием является то, что для вызова метода необходим аксессор ( <expression>.functionProperty() или <expression>[‘functionProperty’]() ), а для вызова функции — нет ( <expression>() ).
this при вызове метода
this — это объект, которому принадлежит метод
При вызове метода, принадлежащего объекту, this становится этим объектом.
Давайте создадим объект, метод которого увеличивает число на 1:
Вызов calc.increment() сделает контекстом функции increment объект calc . Поэтому можно спокойно использовать this.num .
Объект JavaScript наследует метод своего прототипа. Когда вызывается метод, унаследованный от объекта, контекстом всё равно является сам объект:
Object.create() создаёт новый объект myDog и создаёт прототип. Объект myDog наследует метод sayName . Когда исполняется myDog.sayName() , myDog является контекстом исполнения.
В синтаксисе ECMAScript 6 class контекст вызова метода — тоже сам объект:
Ловушка: отделение метода от его объекта
Метод объекта можно переместить в отдельную переменную. При вызове метода с использованием этой переменной вы можете подумать, что this — это объект, в котором определён метод.
На самом деле, если метод вызван без объекта, происходит вызов функции, и this становится глобальным объектом window или undefined . Создание связанной функции исправляет контекст — им становится объект, в котором содержится метод.
Следующий пример создаёт конструктор Animal и его экземпляр — myCat . Затем через 1 секунду setTimeout() логирует информацию об объекте myCat :
Вы можете подумать, что setTimeout вызовет myCat.logInfo() , которая запишет информацию об объекте myCat . Но метод отделяется от объекта, когда передаётся в качестве параметра: setTimout(myCat.logInfo) , и через секунду происходит вызов функции. Когда logInfo вызывается как функция, this становится глобальным объектом или undefined (но не объектом myCat ), поэтому информация об объекте выводится некорректно.
Функцию можно связать с объектом, используя метод .bind() . Если отделённый метод связан с объектом myCat , проблема контекста решается:
myCat.logInfo.bind(myCat) возвращает новую функцию, исполняемую в точности как logInfo , но this которой остаётся myCat даже в случае вызова функции.
Вызов конструктора
Вызов конструктора совершается, когда за ключевым словом new следует выражение, расцениваемое как объект функции, и пара скобок с разделённым запятыми списком аргументов. Пример: new RegExp(‘\\d’) .
В этом примере объявляется функция Country , которая затем вызывается в качестве конструктора:
new Country(‘France’, false) — это вызов конструктора функции Country . Результатом исполнения является новые объект, чьё поле name равняется ‘France’ .
Если конструктор вызван без аргументов, скобки можно опустить: new Country .
Начиная с ECMAScript 6, JavaScript позволяет определять конструкторы ключевым словом class :
new City(‘Paris’) — это вызов конструктора. Инициализация объекта управляется специальным методом класса: constructor , this которого является только что созданным объектом.
Вызов конструктора создаёт новый пустой объект, наследующий свойства от прототипа конструктора. Ролью функции-конструктора является инициализация объекта. Как вы уже знаете, контекст этого типа вызова называется экземпляром. Это — тема следующей главы.
Когда перед аксессором myObject.myFunction идёт ключевое слово new , JavaScript совершит вызов конструктора, а не метода. Возьмём в качестве примера new myObject.myFunction() : сперва при помощи аксессора extractedFunction = myObject.myFunction функция извлекается, а затем вызывается как конструктор для создания нового объекта: new extractedFunction() .
this в вызове конструктора
this — это только что созданный объект
Контекстом вызова конструктора является только что созданный объект. Он используется для инициализации объекта данными из аргументом функции-конструктора.
Давайте проверим контекст в следующем примере:
new Foo() делает вызов конструктора с контекстом fooInstance . Объект инициализируется внутри Foo : this.property задаётся значением по умолчанию.
Тоже самое происходит при использовании class , только инициализация происходит в методе constructor :
Когда исполняется new Bar() , JavaScript создаёт пустой объект и делает его контекстом метода constructor . Теперь вы можете добавлять свойства, используя this : this.property = ‘Default Value’ .
Ловушка: как не забыть про new
Некоторые функции JavaScript создают экземпляры при вызове не только в качестве конструктора, но и функции. Например, RegExp :
При исполнении new RegExp(‘\\w+’) и RegExp(‘\\w+’) JavaScript создаёт эквивалентные объекты регулярных выражений.
Использование вызова функции для создания объектов потенциально опасно (если опустить фабричный метод), потому что некоторые конструкторы могут не инициализировать объект при отсутствии ключевого слова new .
Следующий пример иллюстрирует проблему:
Vehicle — это функция, задающая свойства type и wheelsCount объекту-контексту. При исполнении Vehicle(‘Car’, 4) возвращается объект car , обладающий корректными свойствами: car.type равен ‘Car’ а car.wheelsCount — 4 . Легко подумать, что всё работает как надо.
Тем не менее, this — это объект window при вызове функции, и Vehicle(‘Car’, 4) задаёт свойства объекта window — упс, что-то пошло не так. Новый объект не создан.
Обязательно используйте оператор new , когда ожидается вызов конструктора:
new Vehicle(‘Car’, 4) работает верно: новый объект создан и инициализирован, поскольку присутствует слово new .
В вызове функции добавлена верификация: this instanceof Vehicle , чтобы убедиться, что у контекста исполнения верный тип объекта. Если this — не Vehicle , генерируется ошибка. Таким образом, если исполняется Vehicle(‘Broken Car’, 3) (без new ), то выбрасывается исключение: Error: Incorrect invocation .
Непрямой вызов
Непрямой вызов производится, когда функция вызывается методами .call() или .apply() .
Функции в JavaScript — объекты первого класса, то есть функция — это объект типа Function .
Из списка методов этой функции два, .call() и .apply() , используются для вызова функции с настраиваемым контекстом:
- Метод .call(thisArg[, arg1[, arg2[, . ]]]) принимает в качестве первого аргумента thisArg контекст вызова, а список аргументов arg1, arg2, . передаётся вызываемой функции.
- Метод .apply(thisArg, [args]) принимает в качестве первого аргумента thisArg контекст вызова, а array-like объект [args] передаётся вызываемой функции в качестве аргумента.
Следующий пример демонстрирует непрямой вызов:
increment.call() и increment.apply() оба вызывают функцию-инкремент с аргументом 10 .
Главным отличием между ними является то, что .call() принимает список аргументов, например, myFunction.call(thisValue, ‘value1’, ‘value2’) , а .apply() принимает эти значения в виде array-like объекта: myFunction.apply(thisValue, [‘value1’, ‘value2’]) .
this при непрямом вызове
this — это первый аргумент .call() или .apply()
Очевидно, что при непрямом вызове this — значение, передаваемое .call() или .apply() в качестве первого аргумента. Пример:
Непрямой вызов может пригодиться, когда функцию нужно вызвать в особом контексте, например, решить проблему при вызове функции, где this — всегда window или undefined . Его также можно использовать для симуляции вызова метода объекта.
Ещё одним примером использования является создание иерархии классов в ES5 для вызова родительского конструктора:
Runner.call(this, name) в Rabbit создаёт непрямой вызов родительской функции для инициализации объекта.
Связанная функция
Связанная функция — это функция, связанная с объектом. Обычно она создаётся из обычной функции при помощи метода .bind() . У двух функций совпадают тела и области видимости, но различаются контексты.
Метод .bind(thisArg[, arg1[, arg2[, . ]]]) принимает в качестве первого аргумента thisArg контекст вызова связанной функции, а необязательный список аргументов arg1, arg2, . передаётся вызываемой функции. Он возвращает новую функцию, связанную с thisArg .
Следующий код создаёт связанную функцию и вызывает её:
multiply.bind(2) возвращает новый объект функции double , который связан с числом 2 . Код и область видимости у multiply и double совпадают.
В отличие от методов .apply() и .call() , сразу вызывающих функцию, метод .bind() возвращает новую функцию, которую впоследствии нужно будет вызвать с уже заданным this .
this в связанной функции
Ролью .bind() является создание новой функции, чей вызов будет иметь контекст, заданный в первом аргументе .bind() . Это — мощный инструмент, позволяющий создавать функции с заранее определённым значением this .
Давайте посмотрим, как настроить this связанной функции:
numbers.getNumbers.bind(numbers) возвращает функцию boundGetNumbers , которая связана с объектом numbers . Затем boundGetNumbers() вызывается с this , равным numbers , и возвращает корректный объект.
Функцию numbers.getNumbers можно извлечь в переменную simpleGetNumbers и без связывания. При дальнейшем вызове функции simpleGetNumbers() задаёт this как window или undefined , а не numbers . В этом случае simpleGetNumbers() не вернет корректное значение.
.bind() создаёт перманентную контекстную ссылку и хранит её. Связанная функция не может изменить контекст, используя .call() или .apply() с другим контекстом — даже повторное связывание не даст эффекта.
Только вызов связанной функции как конструктора может изменить контекст, но это не рекомендуется (используйте нормальные функции).
В следующем примере сперва объявляется связанная функция, а затем производится попытка изменить контекст:
Только new one() изменяет контекст связанной функции, в остальных типах вызова this всегда равен 1 .
Стрелочная функция
Стрелочная функция нужна для более короткой формы объявления функции и лексического связывания контекста.
Её можно использовать следующим образом:
Стрелочные функции используют облегчённый синтаксис, убирая ключевое слово function . Можно даже опустить return , когда у функции есть лишь одно выражение.
Стрелочная функция анонимна, что означает, что её свойство name — пустая строка » . Таким образом, у неё нет лексического имени, которое нужно для рекурсии и управления хэндлерами.
Кроме того, она не предоставляет объект arguments , в отличие от обычной функции. Тем не менее, это можно исправить, используя rest-параметры ES6:
this в стрелочной функции
this — это контекст, в котором определена стрелочная функция
Стрелочная функция не создаёт свой контекст исполнения, а заимствует this из внешней функции, в которой она определена.
Следующий пример показывает прозрачность контекста:
setTimeout вызывает стрелочную функцию в том же контексте (метод myPoint ), что и метод log() . Как мы видим, стрелочная функция «наследует» контекст той функции, в которой определена.
Если попробовать использовать в этом примере обычную функцию, она создаст свой контекст ( window или undefined ). Поэтому для того, чтобы код работал корректно, нужно вручную привязать контекст: setTimeout(function() <. >.bind(this)) . Это громоздко, поэтому проще использовать стрелочную функцию.
Если стрелочная функция определена вне всех функций, её контекст — глобальный объект:
Стрелочная функция связывается с лексическим контекстом раз и навсегда. this нельзя изменить даже при помощи метод смены контекста:
Функция, вызываемая непрямым образом с использованием .call(numbers) , задаёт this значение numbers . Стрелочная функция get также получает numbers в качестве this , поскольку принимает контекст лексически. Неважно, как вызывается get , её контекстом всегда будет numbers . Непрямой вызов с другим контекстом (используя .call() или .apply() ), повторное связывание (с использованием .bind() ) не принесут эффекта.
Стрелочную функцию нельзя использовать в качестве конструктора. Если вызвать new get() , JavaScript выбросит ошибку: TypeError: get is not a constructor .
Ловушка: определение метода стрелочной функцией
Вы можете захотеть использовать стрелочную функцию для объявления метода. Справедливо: их объявления гораздо короче по сравнению с обычным выражением: (param) => <. >вместо function(param) <..>.
В этом примере демонстрируется определение метода format() класса Period с использованием стрелочной функции:
Так как format — стрелочная функция, определённая в глобальном контексте, её this — это объект window . Даже если format исполняется в качестве метода объекта walkPeriod.format() , window остаётся контекстом вызова. Так происходит, потому что стрелочная функция имеет статический контекст, не изменяемый другими типами вызовов.
this — это window , поэтому this.hours и this.minutes становятся undefined . Метод возвращает строку ‘undefined hours and undefined minutes’ , что не является желаемым результатом.
Функциональное выражение решает проблему, поскольку обычная функция изменяет свой контекст в зависимости от вызова:
walkPeriod.format() — это вызов метода с контекстом walkPeriod . this.hours принимает значение 2 , а this.minutes — 30 , поэтому метод возвращает корректный результат: ‘2 hours and 30 minutes’ .
Заключение
Поскольку вызов функции имеет наибольшее влияние на this , отныне не спрашивайте:
а спрашивайте:
А в случае со стрелочной функцией спросите:
Каков this там, где объявлена стрелочная функция?
Типы данных

Типы данных в JavaScript можно разделить на две категории: простые типы и объекты. К категории простых типов в языке JavaScript относятся числа, текстовые строки и логические (или булевы) значения.

Специальные значения null и undefined являются элементарными значениями, но они не относятся ни к числам, ни к строкам, ни к логическим значениям. Каждое из них определяет только одно значение своего собственного специального типа.
Любое значение в языке JavaScript, не являющееся числом, строкой, логическим значением или специальным значением null или undefined, является объектом. (т.е. член объектного типа данных) представляет собой коллекцию свойств, каждое из которых имеет имя и значение (либо простого типа, такое как число или строка, либо объектного).
Обычный объект JavaScript представляет собой неупорядоченную коллекцию именованных значений. Кроме того, в JavaScript имеется объект специального типа, известный как массив, представляющий упорядоченную коллекцию пронумерованных значений. Для работы с массивами в языке JavaScript имеются специальные синтаксические конструкции.
В JavaScript определен еще один специальный тип объекта, известный как функция. — это объект, с которым связан выполняемый код. Функция может вызываться для выполнения определенной операции и возвращать вычисленное значение. Подобно массивам, функции ведут себя не так, как другие виды объектов, и в JavaScript определен специальный синтаксис для работы с ними. Одна из важнейших особенностей функций в JavaScript состоит в том, что они являются самыми настоящими значениями, и программы JavaScript могут манипулировать ими, как обычными объектами.
Функции, которые пишутся для инициализации вновь создаваемых объектов (с оператором new), называются конструкторами. Каждый конструктор определяет — множество объектов, инициализируемых этим конструктором. Классы можно представлять как подтипы объектного типа.
В дополнение к классам Array и Function в базовом языке JavaScript определены еще три полезных класса. Класс Date определяет объекты, представляющие даты. Класс RegExp определяет объекты, представляющие регулярные выражения (мощный инструмент сопоставления с шаблоном). А класс Error определяет объекты, представляющие синтаксические ошибки и ошибки времени выполнения, которые могут возникать в программах на языке JavaScript. Имеется возможность определять собственные классы объектов, объявляя соответствующие функции-конструкторы.
Далее мы рассмотрим простые типы данных, а к объектам перейдем в следующих статьях.
Числа
В отличие от многих языков программирования, в JavaScript не делается различий между целыми и вещественными значениями. Все числа в JavaScript представляются вещественными значениями (с плавающей точкой). Для представления чисел в JavaScript используется 64-битный формат, определяемый стандартом IEEE 754. Этот формат способен представлять числа в диапазоне от ±1,8 x 10 308 до ±5 x 10 -324 .
В JavaScript целые десятичные числа записываются как последовательность цифр. Помимо десятичных целых литералов JavaScript распознает шестнадцатеричные значения. Шестнадцатеричные литералы начинаются с последовательности символов «0x», за которой следует строка шестнадцатеричных цифр. Шестнадцатеричная цифра — это одна из цифр от 0 до 9 или букв от A до F, представляющих значения от 10 до 15:
Литералы вещественных чисел должны иметь десятичную точку — при определении таких литералов используется традиционный синтаксис вещественных чисел. Вещественное значение представляется как целая часть числа, за которой следуют десятичная точка и дробная часть числа.
Литералы вещественных чисел могут также представляться в экспоненциальной нотации: вещественное число, за которым следует буква e (или E), а затем необязательный знак плюс или минус и целая экспонента. Такая форма записи обозначает вещественное число, умноженное на 10 в степени, определяемой значением экспоненты:
Арифметические операции
Обработка чисел в языке JavaScript выполняется с помощью арифметических операторов. В число таких операторов входят: оператор сложения +, оператор вычитания — , оператор умножения *, оператор деления / и оператор деления по модулю % (возвращает остаток от деления).
Помимо этих простых арифметических операторов JavaScript поддерживает более сложные математические операции, с помощью функций и констант, доступных в виде свойств объекта Math:
Арифметические операции в JavaScript не возбуждают ошибку в случае переполнения, потери значащих разрядов или деления на ноль. Если результат арифметической операции окажется больше самого большого представимого значения (переполнение), возвращается специальное значение «бесконечность», которое в JavaScript обозначается как Infinity. Аналогично, если абсолютное значение отрицательного результата окажется больше самого большого представимого значения, возвращается значение «отрицательная бесконечность», которое обозначается как -Infinity.
Эти специальные значения, обозначающие бесконечность, ведут себя именно так, как и следовало ожидать: сложение, вычитание, умножение или деление бесконечности на любое значение дают в результате бесконечность (возможно, с обратным знаком).
Потеря значащих разрядов происходит, когда результат арифметической операции оказывается ближе к нулю, чем минимально возможное значение. В этом случае возвращается число 0. Если потеря значащих разрядов происходит в отрицательном результате, возвращается специальное значение, известное как «отрицательный ноль». Это специальное значение практически ничем не отличается от обычного нуля, и у программистов на JavaScript редко возникает необходимость выделять его.
Деление на ноль не считается ошибкой в JavaScript: в этом случае просто возвращается бесконечность или отрицательная бесконечность. Однако есть одно исключение: операция деления нуля на ноль не имеет четко определенного значения, поэтому в качестве результата такой операции возвращается специальное значение «не число» (not-a-number), которое обозначается как NaN. Значение NaN возвращается также при попытке разделить бесконечность на бесконечность, извлечь квадратный корень из отрицательного числа или выполнить арифметическую операцию с нечисловыми операндами, которые не могут быть преобразованы в числа.
В JavaScript имеются предопределенные глобальные переменные Infinity и NaN, хранящие значения положительной бесконечности и «не число». В стандарте ECMAScript 3 эти переменные доступны для чтения/записи и могут изменяться в программах. Стандарт ECMAScript 5 исправляет эту оплошность и требует, чтобы эти переменные были доступны только для чтения.
Дата и время
В базовом языке JavaScript имеется конструктор Date() для создания объектов, представляющих дату и время. Эти объекты Date обладают методами для выполнения простых вычислений с участием дат. Объект Date не является фундаментальным типом данных, как числа.
Конструктор Date() без аргументов создает объект Date со значением, равным текущим дате и времени. Если конструктору передается единственный числовой аргумент, он используется как внутреннее числовое представление даты в миллисекундах, аналогичное значению, возвращаемому методом getTime(). Когда передается один строковый аргумент, он рассматривается как строковое представление даты в формате, принимаемом методом Date.parse().
Кроме того, конструктору можно передать от двух до семи числовых аргументов, задающих индивидуальные поля даты и времени. Все аргументы, кроме первых двух — полей года и месяца, — могут отсутствовать. Обратите внимание: эти поля даты и времени задаются на основе локального времени, а не времени UTC (Universal Coordinated Time — универсальное скоординированное время). В качестве альтернативы может использоваться статический метод Date.UTC(). Date() может также вызываться как функция (без оператора new). При таком вызове Date() игнорирует любые переданные аргументы и возвращает текущие дату и время.
| Аргумент | Обозначение |
|---|---|
| миллисекунды | Количество миллисекунд между нужной датой и полночью 1 января 1970 года (UTC). Например, передав в качестве аргумента число 5000, мы создадим дату, обозначающую пять секунд после полуночи 1 января 1970 года. |
| строка_даты | Единственный аргумент, задающий дату и (необязательно) время в виде строки. Строка должна иметь формат, понятный для Date.parse(). |
| год | Год в виде четырех цифр. Например, 2001 для 2001 года. Для совместимости с более ранними реализациями JavaScript к аргументу добавляется 1900, если значение аргумента находится между 0 и 99. |
| месяц | Месяц, заданный в виде целого от 0 (январь) до 11 (декабрь). |
| день | День месяца, заданный в виде целого от 1 до 31. Обратите внимание, что наименьшее из значений этого аргумента равно 1, а остальных аргументов — 0. Необязательный аргумент. |
| часы | Часы, заданные в виде целого от 0 (полночь) до 23 (11 часов вечера). Необязательный аргумент. |
| минуты | Минуты в часах, указанные в виде целого от 0 до 59. Необязательный аргумент. |
| секунды | Секунды в минутах, указанные в виде целого от 0 до 59. Необязательный аргумент. |
| мс | Миллисекунды в секунде, указанные в виде целого от 0 до 999. Необязательный аргумент. |
У объекта Date нет доступных для записи или чтения свойств; вместо этого доступ к значениям даты и времени выполняется через методы. Большинство методов объекта Date имеют две формы: одна для работы с локальным временем, другая — с универсальным временем (UTC или GMT). Если в имени метода присутствует строка «UTC», он работает с универсальным временем.
Методы объекта Date могут вызываться только для объектов типа Date и генерируют исключение TypeError, если вызывать их для объектов другого типа.
| Метод | Описание |
|---|---|
| getDate(), getUTCDate(), setDate(), setUTCDate() | Возвращает/устанавливает день месяца из объекта Date в соответствии с локальным или универсальным временем. |
| getDay(), getUTCDay() | Возвращает день недели из объекта Date в соответствии с локальным или универсальным временем. |
| getFullYear(), getUTCFullYear(), setFullYear(), setUTCFullYear() | Возвращает/устанавливает год даты в полном четырехзначном формате в локальном или универсальном времени. |
| getHours(), getUTCHours(), setHours(), setUTCHours() | Возвращает/устанавливает поле часов в объекте Date в локальном или универсальном времени. |
| getMilliseconds(), getUTCMilliseconds(), setMilliseconds(), setUTCMilliseconds() | Возвращает/устанавливает поле миллисекунд в объекте Date в локальном или универсальном времени. |
| getMinutes(), getUTCMinutes(), setMinutes(), setUTCMinutes() | Возвращает/устанавливает поле минут в объекте Date в локальном или универсальном времени. |
| getMonth(), getUTCMonth(), setMonth(), setUTCMonth() | Возвращает/устанавливает поле месяца в объекте Date в локальном или универсальном времени. |
| getSeconds, getUTCSeconds(), setSeconds, setUTCSeconds() | Возвращает/устанавливает поле секунд в объекте Date в локальном или универсальном времени. |
| getTime(), setTime() | Возвращает/устанавливает внутреннее представление (миллисекунды) объекта Date. Обратите внимание: это значение не зависит от часового пояса, следовательно, отдельный метод getUTCTime() не нужен. |
| getTimezoneOffset() | Возвращает разницу в минутах между локальным и универсальным представлениями даты в минутах. Обратите внимание: возвращаемое значение зависит от того, действует ли для указанной даты летнее время. |
| getYear(), setYear() | Возвращает/устанавливает поле года в объекте Date. Признаны устаревшими, рекомендуется вместо них применять методы getFullYear() и setFullYear(). . |
| toDateString() | Возвращает строку, представляющую дату из Date для локального часового пояса. |
| toGMTString() | Преобразует Date в строку, беря за основу часовой пояс GMT. Признан устаревшим, вместо него рекомендован метод toUTCString(). |
| toISOString() | Преобразует Date в строку, используя стандарт ISO-8601, объединяющий формат представления даты/времени и UTC. |
| toJSON() | Сериализует объект Date в формат JSON с помощью метода toISOString(). |
| toLocaleDateString() | Возвращает строку, представляющую дату из Date в локальном часовом поясе в соответствии с локальными соглашениями по форматированию дат. |
| toLocaleString() | Преобразует Date в строку в соответствии с локальным часовым поясом и локальными соглашениями о форматировании дат. |
| toLocaleTimeString() | Возвращает строку, представляющую время из Date в локальном часовом поясе на основе локальных соглашений о форматировании времени. |
| toString() | Преобразует Date в строку в соответствии с локальным часовым поясом. |
| toTimeString() | Возвращает строку, представляющую время из Date в локальном часовом поясе. |
| toUTCString() | Преобразует Date в строку, используя универсальное время. |
| valueOf() | Преобразует объект Date в его внутренний миллисекундный формат. |
В дополнение к перечисленным методам экземпляра в объекте Date определены три статических метода. Эти методы вызываются через сам конструктор Date(), а не через отдельные объекты Date:
Date.now()
Возвращает текущее время в миллисекундах.
Date.parse()
Анализирует строковое представление даты и времени и возвращает внутреннее представление этой даты в миллисекундах.
Date.UTC()
Возвращает представление указанной даты и времени UTC в миллисекундах.
Объект Date — это тип данных, встроенный в язык JavaScript. Объекты Date создаются с помощью представленного ранее синтаксиса new Date().
После создания объекта Date можно воспользоваться его многочисленными методами. Многие из методов позволяют получать и устанавливать поля года, месяца, дня, часа, минуты, секунды и миллисекунды в соответствии либо с локальным временем, либо с временем UTC (универсальным, или GMT). Метод toString() и его варианты преобразуют даты в понятные для восприятия строки.
getTime() и setTime() преобразуют количество миллисекунд, прошедших с полуночи (GMT) 1 января 1970 года, во внутреннее представление объекта Date и обратно. В этом стандартном миллисекундном формате дата и время представляются одним целым, что делает дату очень простой арифметически. Стандарт ECMAScript требует, чтобы объект Date мог представить любые дату и время с миллисекундной точностью в пределах 100 миллионов дней до и после 01.01.1970. Этот диапазон равен ±273 785 лет, поэтому JavaScript-часы будут правильно работать до 275 755 года.
Примеры использования объекта Date
Известно множество методов, позволяющих работать с созданным объектом Date:
Ниже показан простой пример часов, использующих объект Date. Здесь используется метод setTimeout() для обновления часов каждую секунду:
Разметка страницы довольно простая и подключает функцию timer() в обработчике события onload() элемента body: