45 вопросов по JavaScript с собеседований

45 вопросов по JavaScript с собеседований

Содержание

Основы языка

  1. Значения и типы
  2. Оператор typeof
  3. Приведение типов (coercion)
  4. Равенство (equality)
  5. null и undefined
  6. Ключевое слово let
  7. Строгий режим (strict mode) и конструкция «use strict»
  8. Полифиллы и шимы (shim)
  9. Транспилирование
  10. Разница между ES5 и ES6

Примитивные значения

  1. Является ли число целым (integer)?
  2. Почему 0.1 + 0.2 === 0.3 — это false?
  3. ! Рекурсивная функция, возвращающая двоичное представление числа
  4. ! Является ли число степенью двойки?
  5. Развернуть все слова в полученной строке
  6. ! Строки-анаграммы
  7. ! Строки-палиндромы
  8. ! Изоморфные строки

Объекты и массивы

  1. Объекты
  2. Сравнение объектов
  3. Массивы
  4. Как очистить массив?
  5. Является ли объект массивом?
  6. ! В массиве чисел найти максимальную разницу между двумя элементами, причем большее число должно обязательно стоять после меньшего
  7. ! Известно, что в неотсортированном массиве содержится (n-1) из n последовательных чисел (границы этого диапазона известны). Найдите пропущенное число за время O(n)
  8. ! Удалить из массива повторяющиеся значения, вернуть только уникальные элементы
  9. ! Реализуйте добавление в очередь и удаление из очереди с помощью двух стеков
  10. ! Найти пересечение массивов
  11. ! В неотсортированном массиве целых чисел найти наибольшее произведение любых трех элементов
  12. ! Рекурсивный бинарный поиск
  13. ! Сбалансированность скобок

Функции и классы

  1. Функция обратного вызова (callback) с простым примером
  2. Область видимости (scope)
  3. Замыкания (closures)
  4. Счетчик с помощью замыкания (closure)
  5. ! Каррирование (закрепление аргумента)
  6. IIFE и паттерн Модуль
  7. Как создать по-настоящему приватный метод класса и в чем недостатки таких методов?
  8. Паттерн Прототип, прототипное наследование
  9. ! Задача по прототипной модели
  10. ! Ключевое слово new
  11. ! Ключевое слово this
  12. ! Привязка контекста функции — метод bind
  13. Как добавить массивам пользовательский метод?

Браузерный JS

  1. Всплытие событий и как его остановить

Основы языка

# Значения и типы

Значения (values) в JavaScript имеют типы, а переменные, в которых лежат эти значения — не имеют.
Встроенные типы данных:

  • string — строки
  • number — числа
  • boolean — логические значения
  • null и undefined
  • object — объекты
  • symbol — символы, появились в ES6

К содержанию

# Оператор typeof

Оператор typeof получает некоторое значение и определяет его тип:

К содержанию

# Приведение типов (coercion)

Конвертация встроенных типов данных друг в друга в JavaScript называется приведением типов (coercion). Оно бывает явное (explicit) и неявное (implicit).

Пример явного приведения:

Пример неявного приведения:

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

Следующие значения приводятся к false:

  • «» (пустая строка)
  • 0, -0, NaN
  • null, undefined
  • false

А эти — к true:

  • «hello»  (любая непустая строка, даже » » — строка с пробелом)
  • 42 (число, отличное от нуля)
  • true
  • [ ], [ 1, «2»] (любой массив, даже пустой)
  • { }, { a: 42 } (любой объект, даже пустой)
  • function foo() { } (любая функция)

К содержанию

# Равенство (equality)

В JavaScript есть два вида сравнения:

Сравнение осуществляется по некоторым простым правилам:

  • Если значения (стороны сравнения) могут принимать значения 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). Количество и порядок символов при этом необходимо учитывать.

  1. egg и sad — неизоморфны,
  2. 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-модели. Когда событие генерируется на элементе, все родительские элементы последовательно о нем оповещаются. Поэтому вы можете повесить обработчик клика на контейнер карточки и таким образом узнать о клике по вложенной кнопке.

Остановить всплытие можно с помощью метода объекта события event.stopPropagation(). В IE < 9 у объекта события есть свойство event.cancelBubble.

К содержанию

Источник: https://github.com/FAQGURU/FAQGURU

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *