9 въпроса задавани при интервю за JavaScrip разработчик

Искате да станете  JavaScript-разработчик? В тази статия ще намерите често задавани въпроси на интервю за позицията. Има и пищовчета…

JavaScript се смята , като подходящ език за начинаещи програмисти. Отчасти поради  факта , че се използва масово при web-разработка , отчасти и заради особенности на езика , които спомагат за изпълнението на неидеален код. Езика не е строг , като всички останали : допуска пропуски на точка и запетая или нежеланието да се притесняваш за разполагаемите ресурси ( пример управление на памета)

Но когато си готов за интервю за JavaScript-разработчик – искаш да бъдеш сигурен , че знаеш тънкости и други интересни неща за езика изпълняващи се автоматически и „задкадров режим“

Та стиганем до началото , тук ще разгледаме най-често срещаните въпроси при интервю за JavaScript разработчик , като може да видите и няколко от тъй нарачените заядли въпроси. Но нека имаме и едно на ум – всяко едно интервю е различно и понякога може да не срещнете нито един от тези въпроси или пък да не бъдат точно така формулирани. Но запомнете най-важното – повече знания никога няма да излишни или да навредят…

Част I. JavaScript-разработчик отговаря на заядливи въпроси.

Тези въпроси са доста полезни при подготовка за интервю: те разкриват колко причудлив е JavaScript и показват решения , които се проявават на пред план при създаване на стила на програмиране. Но нека разгледаме няколко примера.

  1. Защо  Math.max() е по-малко от  Math.min()?
  • Това че Math.max() > Math.min() връща falseзвучи доста неправилно, но въпреки това има голям смисъл: Ако нямаме посочени аргументи в Math.min(), то тя връща безкрайност infinity, а Math.max() връща -infinity.  Това част от спецификацията за методите  max() и min(), но при избора винаги стои логиката. Да , разгладеме следния код:
  • Ако -infinity се смята, като аргумент по подразбиране за  Math.min(),  то всеки един резултат ще бъде равен на -infinity, което е безполезно. Понеже аругмента по подразбиране е равен на infinity, добавянето на друг аргумент би върнало това число в – желаното поведение.

2. Защо 0.1 + 0.2 === 0.3 връща false?

Накратко: това е свързано с факта , как точно JavaScript пази числата с плаваща запетая в двоичен вид.Ако въдевете следния ред в конзолота на Google Chrome, ще получим:

0.1 + 0.2
// 0.30000000000000004
0.1 + 0.2 0.2
// 0.10000000000000003
0.1 + 0.7
// 0.7999999999999999

Това едвам ли ще е проблем при прости изчисления , където има нужда от висока степен на точност , но ще предизивка големи главоболия при несложни приложения , когато изпълняват проверка за равенство. Решението:

  • Фиксированная точка

Ако например : ни е известна максималната точност , която ни е неободима ( да речем при работа с валути) ще използваме цяло число за запазване на стойности. По този начин вместо 4,99долара ще запишем  499 и ще се изпозлват във всички уравнения  с тази си стойност. След ще изкарваме резултата към крайния потребител , използвайки да речем следния ред  result = (value / 100).toFixed(2), който връща искания от нас резултат в приемлив вид.

  • Двоично-десетичен код

Ако точноността е наистина важна , друг вариант е използването на формата на двоично-десетичен код  (BCD) към който може да се обърнем в JavaScript, използвайки  библиотекa BCD . Всяко десетично значение се пази в един байт (8 бита). За съжеление това е неефективно, защото се байта пази 16 отделни стойсноти , а тази система използва стойности от  0 до 9 . Но ако това зависи точността на приложение , ще се наложи да се прибегне към това компромисно решение.

  • Защо 018 минус 017 е равно 3?

Това че  018-017 връща 3  е резултат на неявно привеждане на типове. В дадения случай става дума за осмични числа.

    • Кратко въведение в осмичните числа:

Вероятно, знаете об использовании двоичных (с основанием 2) и шестнадцатеричных (с основанием 16) систем счисления в вычислительной технике, но восьмеричная (с основанием 8) также занимает видное место в истории компьютеров. В конце 1950-х и в 1960-х годах она использовалась для сокращения двоичного кода, что позволило снизить материальные затраты на дорогостоящие в производстве системы!

Шестнадцатеричная пришла вскоре после этого:

IBM 360 [выпущенная в 1965 году] сделала окончательный шаг от восьмеричной системы счисления до шестнадцатеричной. Тех, кто привык к восьмеричной, шокировала экстравагантность! – Вон Пратт

Восьмеричные числа сегодня

Но чем восьмеричные числа полезны в современных языках программирования? Восьмеричные числа иногда предпочтительнее шестнадцатеричных, поскольку не требуют никаких нечисловых цифр (используются 0-7, а не 0-F).

Распространённый способ применения – права доступа к файлам для Unix-систем, где ровно восемь вариантов разрешений:

По тем же причинам они также используются для цифровых дисплеев.

Возвращение к вопросу

В JavaScript префикс 0 преобразует число в восьмеричное. Однако 8 не используется в восьмеричном виде, и любое число, содержащее 8, будет автоматически преобразовано в десятичное число.

Следовательно, 018 - 017 по сути эквивалентно десятичному выражению 18 - 15, потому что 017 – восьмеричное число, а 018 – десятичное.

Часть II: JavaScript-разработчик отвечает на общие вопросы

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

4. Чем отличается выражение функции (Function Expression) от объявления функции (Function Declaration)?

Объявление функции использует ключевое слово function, за которым следует имя функции. Напротив, выражение функции начинается с varlet или const, за которым следует имя функции и оператор присваивания =. Вот примеры:

При использовании главное отличие состоит в том, что объявления функций «поднимаются» (hoisted), а выражение функций – нет. Это означает, что объявления функций перемещаются интерпретатором JavaScript в верхнюю часть области видимости. Поэтому используем объявление функции и вызываем эту функцию в любом месте кода. Напротив, выражение функции вызывается только в линейной последовательности: определяется перед вызовом.

Сегодня JavaScript-разработчик предпочитает выражения функций по двум причинам:

  • В первую очередь, функциональные выражения помогают создать более предсказуемую, структурированную кодовую базу. Конечно, этого можно достичь также и с объявлениями за счёт простоты избавления от грязного кода.
  • Второе, используется синтаксис ES6 для выражений функций. Как правило, получается более кратко, а let и const предоставляют больше контроля над тем, можно ли переназначить переменную или нет, как увидим в следующем вопросе.

5. Чем отличаются varlet и const?

Полагаем, что этот вопрос стал распространённым после выпуска ES6, когда компании в полной мере использовали новый синтаксис. var – ключевое слово для объявления переменной с самого первого выпуска JavaScript. Но его недостатки привели к принятию двух новых ключевых слов в ES6: let и const.

У этих трёх ключевых слов разные подходы к присваиванию, поднятию (hoisting) и области видимости, поэтому рассмотрим каждое из свойств отдельно.

I) Присваивание

Главное различие заключается в том, что let и var можно переприсвоить, а const – нет. Поэтому const подходит для переменных, которые не требуют изменения, и предотвращает случайное переприсваивание. Обратите внимание, что const допускает мутацию переменных. Это означает, что объявленные массив или объект могут измениться. Просто не получится переназначить саму переменную.

И let, и var допускают повторное присваивание, но, как увидим в следующих пунктах, у let ощутимое преимущество по сравнению с var. Поэтому выбираем его в большинстве или даже во всех случаях, когда переменная изменяется.

II) Поднятие (Hoisting)

Аналогично разнице между объявлениями и выражениями функций (обсуждалась выше), переменные, объявленные с использованием var, всегда поднимаются в верхнюю часть соответствующей области видимости. А переменные, объявленные с использованием const и let, не поднимаются. Таким образом, var больше подвергается ошибкам, таким как случайное переназначение. Возьмём пример:

Здесь результат foo() и console.log(x) соответствует ожиданиям. Но что, если забыли второй var?

Несмотря на определение внутри функции, x = "область видимости функции"перезаписывает глобальную переменную. Нужно повторить ключевое слово var, чтобы указать, что вторая переменная x ограничивается только функцией foo().

III) Область видимости

Пока var ограничивается областью видимости функции, let и const доступны в области видимости блока. Блок – это любой код в фигурных скобках {}, включая функции, условные операторы и циклы. Чтобы проиллюстрировать разницу, обратимся к коду:

В нашем условном блоке переменная из глобальной области видимости var aпереопределяется, а глобальные let b и const c – нет. Когда локальные присваивания остаются локальными, код чище и ошибок меньше.

6. Что произойдёт, если JavaScript-разработчик определит переменную без ключевого слова?

Что, если определим переменную без использования ключевого слова вообще? Технически, если x ещё не определена раньше, то x = 1 будет сокращением для window.x = 1. Это частая причина утечек памяти в JavaScript.

Чтобы избежать этого сокращения, используйте строгий режим – введённый в ES5 – напишите use strict вверху документа или конкретной функции. Затем, при попытке объявить переменную без ключевого слова, получим ошибку: Uncaught SyntaxError: Unexpected indentifier.

7. В чём разница между объектно-ориентированным программированием (ООП) и функциональным программированием (ФП)?

JavaScript – это мультипарадигмальный язык, что означает поддержку нескольких стилей программирования, включая событийно-ориентированный, функциональный и объектно-ориентированный.

Среди массы парадигм программирования сейчас выделяются два самых популярных стиля – функциональное программирование (ФП) и объектно-ориентированное программирование (ООП). И JavaScript поддерживает оба.

Объектно-ориентированное программирование

ООП базируется на понятии «объекты». Это структуры данных, которые содержат поля данных – известные в JavaScript как «свойства» – и процедуры – известные как «методы».

Некоторые из встроенных объектов JavaScript используют эту парадигму, включая Math (для методов randommax и sin), JSON (для анализа данных JSON) и примитивные типы данных, такие как StringArrayNumber и Boolean.

Когда полагаемся на встроенные методы, прототипы или классы, то по существу используем объектно-ориентированное программирование.

Функциональное программирование

ФП основывается на концепции «чистых функций», в которых нет разделяемого состояния, изменяемых данных и побочных эффектов. Это кажется заумным, но вероятно, вы создавали много чистых функций в коде.

При одинаковых входных параметрах чистая функция возвращает один и тот же результат. У неё нет побочных эффектов: например, логирования в консоль или изменения внешней переменной, помимо возвращения результата.

Что касается разделяемого состояния, рассмотрим краткий пример, где такое состояние изменяет результат функции, даже если входные параметры совпадают. Добавим сценарий с двумя функциями: одна для прибавления 5 к числу, а другая для умножения числа на 5.

Если вызовем сначала add5, а затем multiply5, результат будет 30. Но если вызовем функции наоборот и запишем результат, то получим другое: 10.

Это идёт вразрез с принципом функционального программирования, так как результат функций различается в зависимости от контекста. Перепишем приведённый выше код так, чтобы результаты стали предсказуемыми:

Теперь значение num.val остаётся равным 1, и независимо от контекста add5(num) и multiply5(num) будут давать один и тот же результат.

8. В чём разница между императивным и декларативным программированием?

Также подумаем о различиях ООП и ФП с точки зрения разницы между «императивным» и «декларативным» программированием.

Это общие термины, которые описывают общие характеристики нескольких парадигм программирования. ФП – пример декларативного программирования, а ООП – пример императивного программирования.

В базовом смысле императивное программирование касается того, как делать. Эта парадигма объясняет шаги конкретным образом и характеризуется циклами for и while, операторами if и switch и так далее.

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

Вот декларативный подход к функции sumArray(), написанной выше.

9. Что такое прототип-ориентированное наследование?

Наконец, мы подошли к прототип-ориентированному наследованию. Стилей объектно-ориентированного программирования несколько, и JavaScript использует прототип-ориентированное наследование. Система организует повторяющееся поведение с использованием действующих объектов – «прототипов».

Даже если идея прототипов окажется новой, JavaScript-разработчик сталкивается с этой системой, когда использует встроенные методы. Например, функции для работы с массивами – mapreducesplice и другие – методы объекта Array.prototype. На самом деле, каждый экземпляр массива (определённый с помощью квадратных скобок [] или, что реже, с использованием new Array()) наследуется от Array.prototype. Поэтому методы вроде mapreduce и splice доступны по умолчанию.

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

В конце цепочки прототипов находим Object.prototype, и почти каждый объект в JavaScript – экземпляр Object.prototypeArray.prototype и String.prototype, например, оба наследуют свойства и методы от Object.prototype.

Чтобы добавить свойства и методы к объекту с использованием синтаксиса прототипа, инициируем объект как функцию и используем ключевое слово prototype для добавления свойств и методов:

Стоит ли переопределять или расширять поведение прототипов?

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

Если хотим, чтобы объекты имели одинаковое поведение, создаём пользовательский объект (или определяем собственный «класс» или «подкласс»), который наследуется от встроенного прототипа без внесения изменений в сам прототип. Если работаем с другими разработчиками, у них отчётливые ожидания относительно поведения JavaScript по умолчанию, и редактирование этого поведения по умолчанию может легко привести к ошибкам.

javascript-разработчик

Однако стоит отметить, что не все разделяют это сильное сопротивление расширению встроенных прототипов. В статье 2005 года создатель JavaScript Брендан Эйх намекнул, что прототипная система на самом деле создавалась отчасти для возможности расширения!

В целом, надеемся, что эти вопросы помогли вам лучше понять JavaScript – как основные функции, так и особенности – и что помогут вам лучше подготовиться к следующему собеседованию.

Если вы JavaScript-разработчик

Недавно проходили собеседование? Хотим узнать больше о вашем опыте! Какие вопросы возникли?

Вдруг вы интервьюер, кажутся ли вам полезными вопросы наподобие перечисленных выше? Или уделяете больше внимания другим областям, таким как технические проблемы или предыдущие проекты?

Оригинал