5 типовых задач на собеседованиях по JavaScript: разбор и решения



    От переводчика: опубликовали для вас статью Марии Перна (Maria Antonietta Perna), которая рассказывает о типовых задачах по JavaScript, чаще всего предлагаемых соискателям-разработчикам на собеседованиях. Статья будет полезна, в первую очередь, начинающим программистам. Ниже даны примеры решения задач, если вам кажется, что они не слишком хороши, и есть вариант получше — предлагайте альтернативу в комментариях.

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

    Причем чаще всего большинство этих задач не имеют отношения к работе, которую будет выполнять соискатель, но решать их все равно нужно. Иногда приходится делать это на доске, без сверки с Google или любым другим источником. Да, ситуация постепенно меняется, и в некоторых компаниях от таких собеседований отказываются, но множество работодателей всё еще придерживается этой традиции. Эта статья посвящена разбору типовых JavaScript-задач, которые часто используются в качестве заданий для соискателей.

    Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

    Skillbox рекомендует: Практический курс «Мобильный разработчик PRO».

    Главное — тщательная подготовка к вашему собеседованию


    Да, прежде чем начать разбирать задачи, давайте рассмотрим общие советы по подготовке к интервью.

    Главное — готовиться заранее. Проверьте, насколько хорошо вы помните алгоритмы и структуры данных, и подтяните знания в тех областях, которые вам не слишком хорошо знакомы. Есть немало онлайн-платформ, которые помогут подготовиться к прохождению собеседований. Советуем GeeksforGeeks, Pramp, Interviewing.io и CodeSignal.

    Стоит научиться проговаривать решение вслух. Желательно рассказывать соискателям о том, что вы делаете, а не просто писать на доске (или же набирать код в компьютере, тоже молча). Таким образом, если в коде вы допустите ошибку, но ход решения будет в общем правильным, можно увеличить свои шансы на успех.

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

    Нужно потренироваться писать код вручную, а не на ПК. Бывает, что на собеседованиях соискателю дают маркер и доску, где нет ни подсказок, ни автоматического форматирования. При поиске решения стоит записать свой код на листе бумаги или прямо на доске. Если держать все в голове, можно забыть что-нибудь важное.

    Шаблонные задачи на JavaScript


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

    Палиндром

    Палиндром — слово, предложение или последовательность символов, которая абсолютно одинаково читается как в привычном направлении, так и в обратном. К примеру, “Anna” — это палиндром, а “table” и “John” — нет.

    Постановка

    Дана строка; нужно написать функцию, которая позволяет вернуть значение true, если строка является палиндромом, и false — если нет. При этом нужно учитывать пробелы и знаки препинания.

    palindrome('racecar') === true
    palindrome('table') === false


    Разбираем задание

    Основная идея здесь — перевернуть строку в обратном направлении. Если «реверсная» строка полностью идентична исходной, значит, мы получили палиндром и функция должна вернуть значение true. Если же нет — false.

    Решение

    Вот код, который позволяет решить палиндром.

    const palindrome = str => {
      // turn the string to lowercase
      str = str.toLowerCase()
      // reverse input string and return the result of the
      // comparisong
      return str === str.split('').reverse().join('')
    }

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

    Второй шаг — реверс строки. Это сделать несложно: необходимо преобразовать ее в массив посредством метода .split() (библиотека String). Потом мы переворачиваем массив, используя .reverse() (библиотека Array). Последний этап — преобразование обратного массива в строку при помощи .join() (библиотека Array).

    Теперь все, что нужно, — сравнить «обратную» строку с исходной, вернув результат true или false.

    FizzBuzz

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

    Постановка

    Требуется написать функцию, выводящую в консоль числа от 1 до n, где n — это целое число, которая функция принимает в качестве параметра, с такими условиями:

    • вывод fizz вместо чисел, кратных 3;
    • вывод buzz вместо чисел, кратных 5;
    • вывод fizzbuzz вместо чисел, кратных как 3, так и 5.

    Пример

    Fizzbuzz(5)

    Результат

    // 1
    // 2
    // fizz
    // 4
    // buzz


    Разбираем задание

    Главное здесь — способ поиска кратных чисел с использованием JavaScript. Его можно реализовать при помощи оператора модуля или же остатка — %, который позволяет показать остаток при делении двух чисел. Если остаток 0, это означает, что первое число кратно второму.

    12 % 5 // 2 -> 12 is not a multiple of 5
    12 % 3 // 0 -> 12 is multiple of 3


    Так, если разделить 12 на 5, получаем 2 с остатком 2. Если же разделить 12 на 3, то получаем 4 с остатком 0. В первом случае 12 не кратно 5, во втором — 12 кратно 3.

    Решение

    Оптимальным решением будет следующий код:

    const fizzBuzz = num => {
      for(let i = 1; i <= num; i++) {
        // check if the number is a multiple of 3 and 5
        if(i % 3 === 0 && i % 5 === 0) {
          console.log('fizzbuzz')
        } // check if the number is a multiple of 3
          else if(i % 3 === 0) {
          console.log('fizz')
        } // check if the number is a multiple of 5
          else if(i % 5 === 0) {
          console.log('buzz')
        } else {
          console.log(i)
        }
      }
    }

    Функция выполняет нужные проверки с использованием условных операторов и выдает результат, необходимый пользователю. В задаче стоит уделить внимание порядку операторов if…else: начинать с двойного условия (&&) и заканчивать случаем, когда кратные числа найти не получилось. В результате мы охватываем все варианты.

    Анаграмма

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

    Постановка

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

    anagram('finder', 'Friend') --> true
    anagram('hello', 'bye') --> false


    Разбираем задание

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

    finder --> f: 1 friend --> f: 1
    i: 1 r: 1
    n: 1 i: 1
    d: 1 e: 1
    e: 1 n: 1
    r: 1 d: 1


    Для хранения данных анаграммы стоит выбрать такую структуру, как объектный литерал JavaScript. Ключ в этом случае — символ буквы, значение — количество ее повторений в текущей строке.

    Есть и другие условия:

    • Нужно убедиться в том, что регистр букв при сравнении не учитывается. Просто преобразуем обе строки в нижний или верхний регистр.
    • Исключаем из сравнения все не-символы. Лучше всего работать с регулярными выражениями.

    Решение

    // helper function that builds the
    // object to store the data
    const buildCharObject = str => {
      const charObj = {}
      for(let char of str.replace(/[^\w]/g).toLowerCase()) {
        // if the object has already a key value pair
        // equal to the value being looped over,
        // increase the value by 1, otherwise add
        // the letter being looped over as key and 1 as its value
        charObj[char] = charObj[char] + 1 || 1
      }
      return charObj
    }
     
    // main function
    const anagram = (strA, strB) => {
      // build the object that holds strA data
      const aCharObject = buildCharObject(strA)
      // build the object that holds strB data
      const bCharObject = buildCharObject(strB)
     
      // compare number of keys in the two objects
      // (anagrams must have the same number of letters)
      if(Object.keys(aCharObject).length !== Object.keys(bCharObject).length) {
        return false
      }
      // if both objects have the same number of keys
      // we can be sure that at least both strings
      // have the same number of characters
      // now we can compare the two objects to see if both
      // have the same letters in the same amount
      for(let char in aCharObject) {
        if(aCharObject[char] !== bCharObject[char]) {
          return false
        }
      }
      // if both the above checks succeed,
      // you have an anagram: return true
      return true
    }

    Обратите внимание на использование Object.keys() в сниппете выше. Этот метод возвращает массив, содержащий имена или ключи в таком же порядке, в каком они встречаются в объекте. В этом случае массив будет таким:

    ['f', 'i', 'n', 'd', 'e', 'r']

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

    Поиск гласных

    Достаточно простая задача, которая часто попадается на собеседованиях.

    Постановка

    Нужно написать функцию, принимающую строку в качестве аргумента и возвращающую количество гласных, которые содержатся в строке.
    Гласными являются «a», «e», «i», «o», «u».

    Пример:

    findVowels('hello') // --> 2
    findVowels('why') // --> 0


    Решение

    Вот самый простой вариант:

    const findVowels = str => {
      let count = 0
      const vowels = ['a', 'e', 'i', 'o', 'u']
      for(let char of str.toLowerCase()) {
        if(vowels.includes(char)) {
          count++
        }
      }
      return count
    }

    Важно обратить внимание на использование метода .includes(). Он доступен и для строк, и для массивов. Его стоит применять для того, чтобы выявить, содержит ли массив определенное значение. Этот метод возвращает true, если массив содержит указанное значение, и false, если нет.

    Есть и более краткое решение проблемы:

    const findVowels = str => {
      const matched = str.match(/[aeiou]/gi)
      return matched ? matches.length : 0
    }

    Здесь задействуется метод .match(), который позволяет реализовать эффективный поиск. Если регулярное выражение как аргумент метода обнаружено внутри указанной строки, то возвращаемым значением становится массив совпадающих символов. Ну а если совпадений нет, то .match() возвращает null.

    Фибоначчи

    Классическая задача, которую можно встретить на собеседованиях самого разного уровня. Стоит напомнить, что последовательность Фибоначчи — это ряд чисел, где каждое последующее является суммой двух предыдущих. Так, первые десять чисел выглядят следующим образом: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34.

    Постановка

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

    fibonacci(3) // --> 2

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

    Решение

    const fibonacci = num => {
      // store the Fibonacci sequence you're going
      // to generate inside an array and
      // initialize the array with the first two
      // numbers of the sequence
      const result = [0, 1]
     
      for(let i = 2; i <= num; i++) {
        // push the sum of the two numbers
        // preceding the position of i in the result array
        // at the end of the result array
        const prevNum1 = result[i - 1]
        const prevNum2 = result[i - 2]
        result.push(prevNum1 + prevNum2)
      }
      // return the last value in the result array
      return result[num]
    }

    В массиве результатов первые два числа содержатся в ряду, поскольку каждая запись в последовательности состоит из суммы двух предыдущих чисел. В самом начале двух чисел, которые можно взять для получения следующего числа нет, поэтому цикл не может сгенерировать их в автоматическом режиме. Но, как мы знаем, первые два числа — всегда 0 и 1. Поэтому инициализировать массив результатов можно вручную.

    Что касается рекурсии, то здесь все проще и сложнее одновременно:

    const fibonacci = num => {
      // if num is either 0 or 1 return num
      if(num < 2) {
        return num
      }
      // recursion here
      return fibonacci(num - 1) + fibonacci(num - 2)
    }

    Мы продолжаем вызывать fibonacci(), передавая все меньшие числа в качестве аргументов. Останавливаемся в случае, когда переданный аргумент равен 0 или 1.

    Вывод


    Скорее всего, вы уже сталкивались с какой-либо из указанных задач, если проходили собеседования на работу frontend- или JavaScript-разработчика (особенно если это уровень junior). Но если они вам и не попадались, то могут пригодиться в будущем — как минимум для общего развития.
    Skillbox рекомендует:

    Skillbox
    169,00
    Онлайн-университет профессий будущего
    Поделиться публикацией

    Комментарии 46

      0
      По поводу анаграмм: я может быть не совсем верно понял условие, но почему бы не нормализовать обе строки вот таким образом, а потом сравнить?
      str.toLowerCase().split('').filter(char => { return char.match(/\w/); }).sort().join('')
        0
        const convert = str => str.toLowerCase().match(/\w/g).sort().join('');
        const anagram = (strA, strB) => convert(strA) === convert(strB);
          0
          На пустой строке выдаст ошибку, match не отработает, равно как и на строке с русскими буквами или знаками препинания, в общем на любой где \w нет.
          Мало того из строки вырежутся, к примеру, русские буквы, и строки 'abc' и 'abcабц' будут равны
            0
            Это проблемы постановки задачи, на это уже отвечали ниже: habr.com/ru/company/skillbox/blog/445360/?reply_to=19946684#comment_19944882
              0
              Ваше решение падает на пустой строке, причем здесь постановка задачи?
                0
                const convert = str => str.toLowerCase().match(/\w/g).sort().join('');
                const anagram = (strA, strB) => strA !== '' && strB !== '' && convert(strA) === convert(strB);

                Так лучше?
                Следует также учесть, что анаграмма это «Слово или словосочетание, образованное путём перестановки букв, составляющих другое слово (или словосочетание).». То есть пустая строка не может быть анаграммой.
        0
        FizzBuzz — мы бы не делали лишних проверок.
        тынц
        Не так
        const fizzBuzz = num => {
          for(let i = 1; i <= num; i++) {
            // check if the number is a multiple of 3 and 5
            if(i % 3 === 0 && i % 5 === 0) {
              console.log('fizzbuzz')
            } // check if the number is a multiple of 3
              else if(i % 3 === 0) {
              console.log('fizz')
            } // check if the number is a multiple of 5
              else if(i % 5 === 0) {
              console.log('buzz')
            } else {
              console.log(i)
            }
          }
        }

        А так
        const fizzBuzz = num => {
          for(let i = 1; i <= num; i++) {
            if(i % 3 !== 0 || i % 5 !== 0) console.log(i);
           else {
            if(i % 3 === 0) console.log('fizz')
            if(i % 5 === 0) console.log('buzz')
          }
        }

          +1
          У вас в выводе fizzbuzz будет размазан на две строки?
            0
            Тогда process.stdout.write
            0
            if(i % 3 !== 0 || i % 5 !== 0) console.log(i);

            А почему ИЛИ? У вас так никогда не будет fizz или buzz, только fizzbuzz, да и то на разных строках.
            Вот так правильно:
            const fizzBuzz = num => {
              for(let i = 1; i <= num; i++) {
                if(i % 3 !== 0 && i % 5 !== 0){ 
                  console.log(i);
                } else {
                  console.log((i % 3 === 0 ? 'fizz' : '') + (i % 5 === 0 ? 'buzz' : ''));
                }
              }
            };
            fizzBuzz(100);
            +2
            FizzBuzz

            Оптимальным решением будет следующий код:

            Ужасная конструкция с нулевой читаемостью. Я даже не буду придираться что совместную кратность можно было проверить как-то как i % (3 * 5), но вложенные if-else читать невозможно и стоит использовать continue.


            Ну а вообще, в языках, откуда эта задачка, видимо, пришла — обычно есть printLn, и тогда первое условие нужно выкинуть вообще, потому что в случае совместной кратности будет fizz и buzz будут выведены на одну и ту же строку и без этого. Я так предполагаю, изначально это и проверялось — догадается ли кандидат до такого поведения, потому что задача, которая просит от тебя написать три условия — ну это не знаю, вступительная задача для пятого класса или типа того.


            Вот код, который позволяет решить палиндром.

            5 копий строки ради задачи, которая решается всего с одной дополнительной переменной. Браво. (Хорошо, к нижнему регистру приводить нужно все равно).


            Второй шаг — реверс строки. Это сделать несложно: необходимо преобразовать ее в массив посредством метода .split() (библиотека String). Потом мы переворачиваем массив, используя .reverse() (библиотека Array). Последний этап — преобразование обратного массива в строку при помощи .join() (библиотека Array).

            Первый раз слышу, чтобы в js кто-то Array и String называл библиотеками, а не встроенными типами, ну да ладно.


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

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


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


            На полиндром можно было бы просить проверить строку, заведомо большую, что даже вторая ее копия не влезет в память, а FizzBuzz — можно ли сократить количество условий.

              +1
              Но оказывается, что js (и много кто) не умеет в хвостовую рекурсию

              В общем-то может, но для этого должны выполняться определенные условия.
              Тут страница 62 и дальше.

              Там как раз Николас рассматривает факториал
              0
              За такое рекурсивное нахождение чисел Фибоначчи лично я бы сразу развернул интервьюируемого на выход как злостного вредителя. И вообще статья жесть.
                +2
                Если вместо этого вы используете рекурсию, это может понравиться интервьюеру и дать вам несколько дополнительных очков.


                const fibonacci = num => {
                // if num is either 0 or 1 return num
                if(num < 2) {
                return num
                }
                // recursion here
                return fibonacci(num — 1) + fibonacci(num — 2)
                }


                Аааа, ужас-ужас-ужас!!!

                Если ваш интервьюер всерьёз считает, что приведённый рекурсивный код лучше тупого цикла — бегите оттуда бегом! Этот код работает за экспоненциальное время, в то время, как цикл — за линейное. В приличных же местах интервьюер должен знать, как эта задача решается за логарифмическое время.
                  0
                  Ну, быстрое возведение в степень матриц — это всё-таки уже немного перебор для JS)
                  0
                  Анаграммы в реальном проекте не используем, поэтому предлагаем вот такую задачку (одну из 3)
                  «написать на javascript (ECMAscript например не старше 5) код, который будет мониторить результаты любых ajax запросов расположенных на той же странице и в случае ошибок отправлять данные на сервер логов/статистики»
                  В ответах мы смотрим на оформление кода, на попытки подключения сторонних либ и самое главное, наличие списка уточняющих вопросов :)
                    0
                    Мне вот интересно как вы fetch будете мониторить?
                      0
                      а ограничение версии ecma не больше 5-й не напрягает?
                        0
                        Меня напрягает что вы собственное условие задачи читать не умеете, где в ней сказано, что на странице, которую надо мониторить, нет fetch? В вашем условии задачи написано, используя es5 написать мониторинг всех ajax. Во-вторых как вам поможет, что кандидат знает или не знает как перекрыть метод send?
                          0
                          Во-вторых как вам поможет, что кандидат знает или не знает как перекрыть метод send?
                          до сих пор помогало :)
                          Тестовое задание вполне себе покрывает часть наших нужд по теме фронтенда. Главное чтобы человек думать умел, остальному — научим/научится
                          0
                          У вас написано «не старше», а не «не больше». 6й версии меньше лет, чем 5й => она моложе.
                      +4
                      По поводу проверки на палиндром.

                      Во-первых, странно, что вообще не упомянуто самое эффективное решение, оно не больно-то сложнее приведённого:
                      function palindrome(str) {
                          for (let i = 0; i < Math.floor(str.length / 2); i++) {
                              if (str[i] !== str[str.length - i - 1]) {
                                  return false;
                              }
                          } 
                      
                          return true;
                      }

                      А во-вторых задачки на проверку на палиндром и вообще на переворот строки простые только до тех пор, пока в дело не вступает Unicode. %)

                      Самый популярное решение работает корректно только для символов из BMP, для остальных разрушаются их суррогатные пары со всеми вытекающими последствиями:
                      const reverse = str => str.split('').reverse().join('');
                      // хабр не может в такие иероглифы ¯\_(ツ)_/¯
                      reverse('A \u{2f804} A');  // 'A �� A' 
                      reverse('A \u{2f804} A') === 'A \u{2f804} A'; // false
                      


                      Это относительно несложно исправить, пользуясь тем, что при итератор по строке обходит code point'ы, а не code unit'ы:
                      const reverse = str => [...str].reverse().join('');
                      reverse('A \u{2f804} A');  // 'A � A' — тут всё ок, мамой клянусь
                      reverse('A \u{2f804} A') === 'A \u{2f804} A'; // true
                      


                      Однако даже это не спасает, когда в дело вступают графемные кластеры:
                      const reverse = str => [...str].reverse().join('');
                      const str = 'А \u{0415}\u{0308} А';  // нормальная такая Ё
                      reverse(str);  // 'А ̈Е А'
                      reverse(str) === str; // true
                      


                      Универсального решения, работающего во всех случаях, я не знаю, увы.
                        0
                        Вот кстати, плюсую. Помимо неэффективного кол-ва операций, split+reverse+join занимает еще лишнюю память, в отличии от способа с двумя указателями, где памяти выделяется только на переменную в цикле.

                        Задача может усложняться разными регистрами и пробелами, вроде «А роза упала на лапу азора».

                        Тогда нужно отдельно двигать правый и левый указатель, (на пробелах предвигать). В целом в статье разбор типовых задач странный. Решения далеко не самые оптимальные и местами даже спорные.
                          0
                          Есть же String.prototype.normalize().
                          [...'А \u{0415}\u{0308} А'.normalize()].reverse().join`` // "А Ё А"
                            0
                            Действительно, я про него как-то не знал (упс). Ценная штука в некоторых случаях.

                            Проблема в том, что не любой графемный кластер можно нормализовать:
                            // devanagari ssi 
                            [...'षि'].length;  // => 2
                            [...'षि'.normalize()].length;  // => 2
                            
                            // ok hand with dark skin tone
                            [...'\u{270c}\u{1f3ff}'].length;  // => 2
                            [...'\u{270c}\u{1f3ff}'.normalize()].length;  // => 2
                          +1
                          итератор по строке обходит code point'ы, а не code unit'ы
                          Это, кстати, приводит к некоторым любопытным последствиям:
                          const str = 'A \u{2f804} A';
                          [...str].length === str.length;  // false
                          [...str].map(char => char.length);  // [1, 1, 2, 1, 1]
                          
                          
                            0
                            Каждый раз, когда я начинаю думать, что знаю JS досконально, находится что-нибудь в этом роде и меня разубеждает)
                            –1
                            palindrome('race car') // false

                            Неплохо бы добавить
                            str = str.toLowerCase().replace(/ /g,'');

                            palindrome('А роза упала на лапу Азора') // true
                            // есть еще вариант от пенсионного фонда
                            palindrome('Оголи жопу пожилого'); // true

                              0
                              Мда, эксперты…
                              Код на проверку палиндрома не сработает, как писали выше, если будут пробелы и знаки препинания.
                              Fizzbuzz решён прямо в тупую, никакого понимания вообще.
                              Анаграммы… странноваты
                              Гласные вообще решаются в одну строку, два действия(если нет дополнительных условий).
                              Только Фибоначчи нормально.
                              Если эта дама людей собеседует, то ей стоит слегка поднять уровень.
                                +1
                                Внесу и я свою лепту в fizzbuzz :)
                                function Fizzbuzz (num) {
                                  let response = '';
                                  if (!(num % 3)) response += 'fizz';
                                  if (!(num % 5)) response += 'buzz';
                                  return response || num;
                                }
                                
                                  0
                                  Написал точно такое же. Очень странно что оптимальным названо то что в статье, как впрочем и решения других задач.

                                  function fizzbuzz(n) {
                                      var i, s; 
                                      for (i = 1; i <= n; i++) {
                                          s = '';
                                          if (i % 3 == 0) s = 'fizz';
                                          if (i % 5 == 0) s += 'buzz';
                                          console.log(s ? s : i);
                                      }
                                  }
                                  


                                  P.S. в задаче требуется написать функцию которая выводит от 1 до n в консоль. Ваша функция этого не делает, и еще со строчной обычно называют конструкторы, это если придираться ;)
                                    0
                                    Согласен с вами.
                                      0
                                      А чем s ? s : i лучше, чем s || i для i ∈ ℕ?
                                        0
                                        Ничем, просто я так написал, потому что мог.
                                      0
                                      Каноничный вариант.
                                      0
                                      В анаграмме у вас две ошибки в одной строке:
                                      for(let char of str.replace(/[^\w]/g).toLowerCase()) { ...
                                      


                                      Во-первых, у метода replace должно быть 2 аргумента, иначе он заменит найденный символ на undefined. Вероятно это опечатка, но надо же проверять примеры, перед тем как публиковать статью.
                                      Во-вторых, данное решение работает только с латинскими символами. В JS, паттерны, вроде \w не рассчитаны на юникод. ИМХО, это самое больное место регулярных выражений в JS. Правильная реализация будет выглядеть как-то так:
                                      const notALetterRegEx = /[^A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶ-ͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԣԱ-Ֆՙա-ևא-תװ-ײء-يٮ-ٯٱ-ۓەۥ-ۦۮ-ۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴ-ߵߺऄ-हऽॐक़-ॡॱ-ॲॻ-ॿঅ-ঌএ-ঐও-নপ-রলশ-হঽৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽૐૠ-ૡଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽଡ଼-ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘ-ౙౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠ-ೡഅ-ഌഎ-ഐഒ-നപ-ഹഽൠ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะา-ำเ-ๆກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-ະາ-ຳຽເ-ໄໆໜ-ໝༀཀ-ཇཉ-ཬྈ-ྋက-ဪဿၐ-ၕၚ-ၝၡၥ-ၦၮ-ၰၵ-ႁႎႠ-Ⴥა-ჺჼᄀ-ᅙᅟ-ᆢᆨ-ᇹሀ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙶᚁ-ᚚᚠ-ᛪᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦩᧁ-ᧇᨀ-ᨖᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᰀ-ᰣᱍ-ᱏᱚ-ᱽᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₔℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℼ-ℿⅅ-ⅉⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-Ɐⱱ-ⱽⲀ-ⳤⴀ-ⴥⴰ-ⵥⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆷㇰ-ㇿ㐀-䶵一-鿃ꀀ-ꒌꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙟꙢ-ꙮꙿ-ꚗꜗ-ꜟꜢ-ꞈꞋ-ꞌꟻ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꤊ-ꤥꤰ-ꥆꨀ-ꨨꩀ-ꩂꩄ-ꩋ가-힣豈-鶴侮-頻並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]|[\ud840-\ud868][\udc00-\udfff]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\ude80-\ude9c\udea0-\uded0\udf00-\udf1e\udf30-\udf40\udf42-\udf49\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf]|\ud801[\udc00-\udc9d]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f\udd00-\udd15\udd20-\udd39\ude00\ude10-\ude13\ude15-\ude17\ude19-\ude33]|\ud808[\udc00-\udf6e]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb]|\ud869[\udc00-\uded6]|\ud87e[\udc00-\ude1d]/ug;
                                      for(let char of str.replace(notALetterRegEx, "").toLowerCase()) { ...
                                      

                                      Выглядит дико, зато работает. Естественно, на собеседовании такое никто по памяти не напишет. Но, как мне кажется, кандидат должен отметить, что решение работает только с латинским алфавитом, а для поддержки других языков понадобится либо отдельная библиотека, такая как XRegExp, позволяющая использовать паттерны вроде p{L}, либо длиннющая регулярка, как правило сгенерированная каким-то сервисом. Например, данную простыню я сгенерировал здесь.
                                        0
                                        Была еще задача на комплементарность ДНК. Решение ниже
                                        const inp = 'ACCGGGTTTT';
                                        const out = 'AAAACCCGGT';
                                        
                                        // A -> T
                                        // C -> G
                                        
                                        // set mirror
                                        // replace A to T
                                        // replace C to G
                                        // replace T to A
                                        // replace G to T
                                        
                                        function dnaComplement(str) {
                                          let newStr = '';
                                        
                                          for (let i = str.length - 1; i >= 0; i--) {
                                            let s = str[i];
                                        
                                            if (s === 'A') {
                                              newStr = newStr + 'T';
                                            } else if (s === 'T') {
                                              newStr = newStr + 'A';
                                            } else if (s === 'C') {
                                              newStr = newStr + 'G';
                                            } else if (s === 'G') {
                                              newStr = newStr + 'C';
                                            }
                                          }
                                        
                                          return newStr;
                                        }
                                        
                                        let resp = dnaComplement(inp);
                                        
                                        console.log('resp ->', resp);
                                        console.log(out === resp ? 'Done' : 'Wrong');


                                        Немного оффтопа
                                        Однажды пригласили меня на собеседование в одну фирму на позицию Senior JS developer (дело было в Кракове, Польша). В начале поговорили о жизни и ни о чем. Плавно перешли к менеджменту и микроменеджменту. И вот один из собеседователей говорит — ну что размялись, давайте теперь сделаем техническое интервью. Ну ок, не вопрос, конечно. И тут парень открывает гугл! А мне был виден его монитор немного, так как за спиной у него стеклянная стена и я видел отражение. И начинает читать вопросы для собеседования по JS с гугла Карл! Ты же сеньора собеседуешь! Ну как же так?!

                                        Нда… теперь эта компания в моем личном черном списке. Нельзя же так не уважать людей, которые к вам приходят.
                                          0
                                          Мне кажется, что анаграммы можно сделать намного проще:
                                           function anagram(srt1, str2) {
                                            const srt1Prepared = srt1.replace(/[^\w\s]|_/g, "").toLowerCase().split('').sort().join('');
                                            const srt2Prepared = str2.replace(/[^\w\s]|_/g, "").toLowerCase().split('').sort().join('');
                                            
                                            return srt1Prepared === srt2Prepared;
                                          }
                                          
                                          console.log(anagram('finder', 'Friend')); // true
                                          console.log(anagram('hello', 'bye')); // false
                                          console.log(anagram('', '')); // true 
                                            0

                                            К слову сказать Object.keys не сохраняет порядок ключей

                                              0

                                              На самом деле, там всё немного сложнее — начиная с ES6 спецификация весьма строго описывает требуемый порядок ключей как для Object.keys(), так и для for (... in ...).
                                              Сначала идут целочисленные свойства (в порядке возрастания), потом строковые (в порядке создания), потом symbols (тоже в порядке создания).


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

                                              0

                                              Мда. Всегда что-то можно оптимизировать. Даже фуз-буз задачку. Другие не стал читать — слишком много слов.


                                              function getFizzBuzz(num) {
                                                  var result = '';
                                                  if ( num % 3 == 0 ) {
                                                      result += 'fizz';
                                                  }
                                                  if ( num % 5 == 0 ) {
                                                      result += 'buzz';
                                                  }
                                                  return result || num;
                                              }
                                                0
                                                const getFizzBuzz = num => `${num % 3 ? '' : 'fizz'}${num % 5 ? '' : 'buzz'}` || num;
                                                  0
                                                  Или давайте ещё извратимся.
                                                  const getFizzBuzz = num => `${'fizz'.repeat(!(num % 3))}${ 'buzz'.repeat(!(num % 5))}` || num;
                                                    0

                                                    Главный посыл не в количестве "крутых" фишек, а в оптимальности алгоритма. "Извращения" (как Вы сказали) не добавляют новизны, но читабельность кода ухудшают.

                                                      0
                                                      Понятие оптимальности зависит от показателей, которые мы хотим оптимизировать. Если мы хотим оптимизировать показатель «продемонстрировать интервьюеру знакомство с фичами ES6 и умение писать write-only код»… =)
                                                        0

                                                        Согласен. Но это уже из области "быстрее, выше, сильнее". В обычной жизни такие успехи не требуются, важнее чистота и лаконичность кода без потери ясности понимания написанного. Я не спорю с Вами, всего лишь дополняю Ваш ответ.

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