Предлагаем вашему вниманию широкий список вопросов, которые могут задать на собеседовании 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-разработчику
Комментарии