Предлагаем вашему вниманию широкий список вопросов, которые могут задать на собеседовании JavaScript-программисту. Все задачи решены на JS, ES5 и ES6.
После каждого решения приведена ссылка на Codepen, где вы можете самостоятельно опробовать решение конкретной задачи.
Массивы
1.1 У вас есть массив целых чисел, найдите наибольшее произведение из трёх чисел данного массива.
var unsortedArray = [-10, 7, 29, 30, 5, -10, -70];
computeProduct(unsortedArray); // 21000
function sortIntegers(a, b) {
return a - b;
}
// Наибольшее произведение - это (min1 * min2 * max1 || max1 * max2 * max3)
function computeProduct(unsorted) {
var sortedArray = unsorted.sort(sortIntegers),
product1 = 1,
product2 = 1,
array_n_element = sortedArray.length - 1;
// Получаем произведение трёх наибольших элементов уже отсортированного массива
for (var x = array_n_element; x > array_n_element - 3; x--) {
product1 = product1 * sortedArray[x];
}
product2 = sortedArray[0] * sortedArray[1] * sortedArray[array_n_element];
if (product1 > product2) return product1;
return product2;
}
Решение на Codepen: https://codepen.io/kennymkchan/pen/LxoMvm?editors=0012
Нахождение пропущенного элемента
1.2 Неотсортированный массив содержит (n-1) чисел из последовательности {1,2,...,n} (границы определены), найдите недостающий элемент массива за время o(n).
// Число, возвращаемое функцией - 8
var arrayOfIntegers = [2, 5, 1, 4, 9, 6, 3, 7];
var upperBound = 9;
var lowerBound = 1;
findMissingNumber(arrayOfIntegers, upperBound, lowerBound); // 8
function findMissingNumber(arrayOfIntegers, upperBound, lowerBound) {
// Проходим через массив и находим сумму чисел
var sumOfIntegers = 0;
for (var i = 0; i < arrayOfIntegers.length; i++) {
sumOfIntegers += arrayOfIntegers[i];
}
/* Находим теоретическую сумму последовательных чисел, используя вариацию суммы Гаусса*/
// Формула: [(N * (N + 1)) / 2] - [(M * (M - 1)) / 2];
// N - верхняя граница, а M - нижняя граница
upperLimitSum = (upperBound * (upperBound + 1)) / 2;
lowerLimitSum = (lowerBound * (lowerBound - 1)) / 2;
theoreticalSum = upperLimitSum - lowerLimitSum;
return theoreticalSum - sumOfIntegers;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/rjgoXw?editors=0012
Удаление повторяющихся значений
1.3 Удалите все одинаковые значения в массиве, возвращая массив, состоящий из уникальных элементов.
// Реализация на ES6
var array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
// Реализация на ES5
var array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
uniqueArray(array); // [1, 2, 3, 5, 9, 8]
function uniqueArray(array) {
var hashmap = {};
var unique = [];
for(var i = 0; i < array.length; i++) {
/* Если возвращаемое значение (unique) - undefined, то оно приравнивается к false. */
if(!hashmap.hasOwnProperty(array[i])) {
hashmap[array[i]] = 1;
unique.push(array[i]);
}
}
return unique;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/ZLNwze?editors=0012
1.4 У вас есть массив целых чисел, найдите наибольшую разность между такими двумя элементами, что элемент с меньшим значением стоит перед элементом с большим значением.
var array = [7, 8, 4, 9, 9, 15, 3, 1, 10];
// [7, 8, 4, 9, 9, 15, 3, 1, 10] возвращаемое значение будет `11`, как разница между `4` и `15`
// Примечание: это не `14`, как разница между `15` и `1`, так как 15 стоит перед 1.
findLargestDifference(array);
function findLargestDifference(array) {
// Если в массиве всего один элемент, то задача не имеет смысла
if (array.length <= 1) return -1;
// currentMin будет отвечать за наименьший элемент
var currentMin = array[0];
var currentMaxDifference = 0;
// Мы будем проходить по массиву раз за разом, находя наибольшую разность
// Записываем наименьший элемент, так как он впоследствии нам пригодится
for (var i = 1; i < array.length; i++) {
if (array[i] > currentMin && (array[i] - currentMin > currentMaxDifference)) {
currentMaxDifference = array[i] - currentMin;
} else if (array[i] <= currentMin) {
currentMin = array[i];
}
}
// Если разность равна нулю или меньше нуля, то задача не имеет смысла
if (currentMaxDifference <= 0) return -1;
return currentMaxDifference;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/MJdLWJ?editors=0012
И ещё задачи на массивы
1.5 У вас есть массив целых чисел, выведите такой массив, что output[i] равен произведению всех элементов массива за исключением i-ого. (Решите за O(n) без операции деления).
var firstArray = [2, 2, 4, 1];
var secondArray = [0, 0, 0, 2];
var thirdArray = [-2, -2, -3, 2];
productExceptSelf(firstArray); // [8, 8, 4, 16]
productExceptSelf(secondArray); // [0, 0, 0, 0]
productExceptSelf(thirdArray); // [12, 12, 8, -12]
function productExceptSelf(numArray) {
var product = 1;
var size = numArray.length;
var output = [];
// Из первого массива: [1, 2, 4, 16]
// В данном случае, последний элемент находится уже на правильном
// месте, чтобы просто умножить его на 1 в следующем шаге
for (var x = 0; x < size; x++) {
output.push(product);
product = product * numArray[x];
}
var product = 1;
for (var i = size - 1; i > -1; i--) {
output[i] = output[i] * product;
product = product * numArray[i];
}
return output;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/OWYdJK?editors=0012
Пересечение двух массивов
1.6 Найдите пересечение двух массивов. Пересечение - это общие элементы, которые присутствуют в обоих массивах. Элементы должны быть уникальны.
var firstArray = [2, 2, 4, 1];
var secondArray = [1, 2, 0, 2];
intersection(firstArray, secondArray); // [2, 1]
function intersection(firstArray, secondArray) {
/* Здесь логика состоит в том, чтобы создать hashmap с элементами
первого массива в качестве ключей */
/* После этого вы можете проверить, существует ли элемент в хэше.
Сделать это можно за время O(1) благодаря hashmap */
// Если элемент существует, то добавляем его к новому массиву
var hashmap = {};
var intersectionArray = [];
firstArray.forEach(function(element) {
hashmap[element] = 1;
});
secondArray.forEach(function(element) {
if (hashmap[element] === 1) {
intersectionArray.push(element);
hashmap[element]++;
}
});
return intersectionArray;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/vgwbEb?editors=0012
Строки
2.1 У вас есть строка. Ваша задача - перевернуть каждое слово в строке. "Welcome to this Javascript Guide!" должно стать "emocleW ot siht tpircsavaJ !ediuG".
var string = "Welcome to this Javascript Guide!";
// Вывод будет таким: "!ediuG tpircsavaJ siht ot emocleW"
var reverseEntireSentence = reverseBySeparator(string, "");
// Вывод будет таким: "emocleW ot siht tpircsavaJ !ediuG"
var reverseEachWord = reverseBySeparator(reverseEntireSentence, " ");
function reverseBySeparator(string, separator) {
return string.split(separator).reverse().join(separator);
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/VPOONZ?editors=0012
2.2 У вас есть две строки. Определите, являются ли они анаграммами друг к другу. "Mary" - анаграмм к "Army".
var firstWord = "Mary";
var secondWord = "Army";
isAnagram(firstWord, secondWord); // true
function isAnagram(first, second) {
// Для удобства переведём все символы в нижний регистр
var a = first.toLowerCase();
var b = second.toLowerCase();
/* Отсортируем строки и присоединим финальный массив к строкам. Сравним результаты */
a = a.split("").sort().join("");
b = b.split("").sort().join("");
return a === b;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/NdVVVj?editors=0012
2.3 Проверьте, является ли строка палиндромом. "racecar" - палиндром. "race car" должен тоже считаться за палиндром. Регистр должен учитываться.
isPalindrome("racecar"); // true
isPalindrome("race Car"); // true
function isPalindrome(word) {
// Уберём все символы, не являющиеся буквами
var lettersOnly = word.toLowerCase().replace(/\s/g, "");
// Сравним строку с её перевёрнутой версией
return lettersOnly === lettersOnly.split("").reverse().join("");
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/xgNNNB?editors=0012
Изоморфные строки
2.4 Проверьте, являются ли две строки изоморфными.
Две строки называются изоморфными, когда в строке A можно заменить конкретный символ на любой другой для получения строки B. Порядок символов должен остаться неизменным. Каждый последовательный символ в строке A сравнивается с каждым последовательным символов в строке B.
'paper' и 'title' вернёт true (p = t, a = i, e = l, r = e).
'egg' и 'sad' вернёт false.
'dgg' и 'add' вернёт true.
isIsomorphic("egg", 'add'); // true
isIsomorphic("paper", 'title'); // true
isIsomorphic("kick", 'side'); // false
function isIsomorphic(firstString, secondString) {
/* Проверка на то, имеют ли две строки одинаковую длину. Если нет, то они не изоморфны по определению. */
if (firstString.length !== secondString.length) return false
var letterMap = {};
for (var i = 0; i < firstString.length; i++) {
var letterA = firstString[i],
letterB = secondString[i];
if (letterMap[letterA] === undefined) {
letterMap[letterA] = letterB;
} else if (letterMap[letterA] !== letterB) {
/* Если letterA уже существует, но не указывает на letterB,
это значит, что A указывает на больше чем одну букву в строке B. */
return false;
}
}
// Если программа вышла из цикла, значит все условия соблюдены!
// Две строки изоморфны!
return true;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/mRZgaj?editors=0012
Стэки и очереди
3.1 Реализуйте два метода: enqueue и dequeue, используя только два стэка
var inputStack = []; // Первый стэк
var outputStack = []; // Второй стэк
// Для реализации enqueue просто сделаем push в первый стэк
function enqueue(stackInput, item) {
return stackInput.push(item);
}
function dequeue(stackInput, stackOutput) {
/* Перевернём стэк таким образом, что первый элемент выходного
стэка - последний элемента входного. После этого делаем pop выходного стэка */
if (stackOutput.length <= 0) {
while(stackInput.length > 0) {
var elementToOutput = stackInput.pop();
stackOutput.push(elementToOutput);
}
}
return stackOutput.pop();
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/mRYYZV?editors=0012
3.2 Напишите функцию, которая будет проверять, являются ли фигурные скобки сбалансированными, используя стэки. {} - считается блоком. {}{} - сбалансированные скобки. {{{}} - несбалансированные.
var expression = "{{}}{}{}"
var expressionFalse = "{}{{}";
isBalanced(expression); // true
isBalanced(expressionFalse); // false
isBalanced(""); // true
function isBalanced(expression) {
var checkString = expression;
var stack = [];
// Если входная строка пуста, то технически всё сбалансировано
if (checkString.length <= 0) return true;
for (var i = 0; i < checkString.length; i++) {
if(checkString[i] === '{') {
stack.push(checkString[i]);
} else if (checkString[i] === '}') {
if (stack.length > 0) {
stack.pop();
} else {
return false;
}
}
}
if (stack.pop()) return false;
return true;
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/egaawj?editors=0012
Рекурсия
4.1 Напишите рекурсивную функцию, которая переводит десятичное число в двоичное. Если входное число - 4, выходным будет 100.
decimalToBinary(3); // 11
decimalToBinary(8); // 1000
decimalToBinary(1000); // 1111101000
function decimalToBinary(digit) {
if(digit >= 1) {
if (digit % 2) {
return decimalToBinary((digit - 1) / 2) + 1;
} else {
return decimalToBinary(digit / 2) + 0;
}
} else {
return '';
}
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/OWYYKb?editors=0012
4.2 Напишите рекурсивную функцию, которая выполняет бинарный поиск
function recursiveBinarySearch(array, value, leftPosition, rightPosition) {
// Value DNE
if (leftPosition > rightPosition) return -1;
var middlePivot = Math.floor((leftPosition + rightPosition) / 2);
if (array[middlePivot] === value) {
return middlePivot;
} else if (array[middlePivot] > value) {
return recursiveBinarySearch(array, value, leftPosition, middlePivot - 1);
} else {
return recursiveBinarySearch(array, value, middlePivot + 1, rightPosition);
}
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/ygWWmK?editors=0012
Числа
5.1 У вас есть целое число, определите, является ли оно является степенью двойки.
isPowerOfTwo(4); // true
isPowerOfTwo(64); // true
isPowerOfTwo(1); // true
isPowerOfTwo(0); // false
isPowerOfTwo(-1); // false
//Без проверки на ноль
function isPowerOfTwo(number) {
// `&` ипользует побитовую запись n.
// В данном случае, входное число - 4; выражение идентично:
// `return (4 & 3 === 0)`
/* 4 в двоичном виде - 100, а 3 - 011. &
проверяет на то, чтобы оба числа в двоичном виде имели еденицу на одном и том же месте.
В таком случае будет возвращено 1, иначе - 0. В этом случае, результатом будет 000. */
// таким образом, 4 удовлетворяет условиям.
return number & (number - 1) === 0;
}
//С проверкой на ноль
function isPowerOfTwoZeroCase(number) {
return (number !== 0) && ((number & (number - 1)) === 0);
}
Решение на Codepen: http://codepen.io/kennymkchan/pen/qRGGeG?editors=0012
JavaScript
6.1 Объясните, что такое "поднятие" в JavaScript.
"Поднятие" - это правило, которое подразумевает под собой необходимость перемещать все объявления переменных в вершину конкретного блока кода. Это касается объявления переменных, инициализация же может проходить и дальше в коде.
6.2 Объясните функционал директивы use strict;
директива use strict; говорит о том, что код должен быть выполнен в "строгом режиме". Один из плюсов данного режима - это то, что он предотвращает использование необъявленных переменных. Наиболее старые версии JavaScript будут игнорировать данную директиву.
// Пример "строгого режима"
"use strict";
catchThemAll();
function catchThemAll() {
x = 3.14; // На этом моменте будет ошибка
return x * x;
}
6.3 Объясните, что такое event bubbling и как его предотвратить.
Even bubbling - это концепт, при котором порядок выполнения событий должен подниматься по структуре DOM-дерева.
Один из вариантов предотвращения - "event.stopPropagation()" или "event.cancelBubble", для IE версии ниже 9.
6.4 Какова разница между == и === в JavaScript?
=== известен как строгий оператор. Ключевая разница между == и === - это то, что == сравнивает лишь значения, а === - ещё и типы данных.
// Пример "==" и "===". 0 == false; // true 0 === false; // false 2 == '2'; // true 2 === '2'; // false
6.5 Какова разница между null и undefined?
Null в JavaScript может быть присвоен переменной, а undefined лишь показывает, что переменная была объявлена, но не была инициализирована.
6.6 В чём разница между прототипным наследованием и классическим наследованием?
В классическом наследовании классы неизменны, поддерживают множественное наследование, могут содержать интерфейсы, final классы и abstract классы. Прототипы же куда более гибки в том плане, что они могут быть изменены.
Другие статьи по теме
Подборка материалов по JavaScript
10 вещей, которые стоит знать каждому JavaScript-разработчику
Комментарии