JS Interview questions & answers

Опишіть прийом делегування подій в JS

Спливання та перехоплення дозволяють нам реалізувати один з найпотужніших шаблонів обробки подій під назвою делегування подій.Ідея в тому, що якщо у нас є багато елементів, які обробляються подібним чином, то замість того, щоб призначати обробник кожному з них, ми ставимо один обробник на їхнього спільного предка. У обробнику ми отримуємо event.target, щоб побачити, де насправді сталася подія та обробити її.

Опишіть як працює this в JavaScript? Наведіть приклад того, як робота з this змінилась в ES6

this — посилання на об'єкт в контексті якого виконується функція. Іншими словами, якщо всередині функції є звернення типу this.им'я_властивості це означає, що ми звертаємося до властивості об'екту в контексті котрого ця функція виконується. Цей механізм дозволяє писати функції, які легко перевикористувати у подальшому.


Дивіться самі:

function getName() {
return this.name;
}
var billy = { name: ‘Billy’};
getName.call(billy); // Billy

var john = { name: ‘John’};
getName.call(john); // John

Тут функція getName нічого не знає про об'єкт, в контексті якого вона буде викликана, проте чудово виконала свою роботу. Важливий момент: контекст виконання прив'язується в момент виклику функції (рядок getName.call(billy)). В ES6 була додана стрілкова функція, контекст виконання якої встановлюється не в момент виклику функції(як в прикладі, наведенному вище), а в момент виконання самої функції.


var getName = () => {
return this.name;
}
var billy = { name: 'Billy'};
getName.call(billy); // undefined

Тут отримуємо undefined замість імені, бо функція визначена в глобальному скоупі, а в несуворому режимі, в момент визначення стрілкової функції, її this буде вказувати на window (в браузері). У window змінна name не визначена, звідси і undefined. Явне визначення контексту виклику (.call()) зі стрілковими функціями не працює

Опишіть як працює прототипно-орієнтована модель наслідування в JS

В плані наслідування JavaScript працює лише з однією сутністю: об'єктами. Кожен об'єкт має внутрішнє посилання на інший об'єкт, званий його прототипом. У об'єкта-прототипа також є свій власний прототип и так далі до тих пір, доки цепочка не завершиться об'єктом, у якого властивість prototype дорівнює null. По визначенню, null не має прототипів і є завершальною ланкою в ланцюжку прототипів. При спробі отримати доступ до будь-якої властивості об'єкта, властивість спочатку шукається в самому об'єкті, після цього в ланцюжку прототипів. Пошук ведеться до тих пір, доки не знайдено властивість з іменем, що збігається або доки кінець ланцюжка прототипів не буде досягнуто.

В чому полягає різниця між значеннями null, undefined і undeclared? Як би ви реалізували перевірку на ці значення?

null — пусте значення, undefined — невизначене значення, undeclared — змінна не була оголошенаа, її виклик викличе помилку, виклик typeof покаже undefined (для реалізації перевірки). Для тестової змінної «a», перевірка може бути реалізована наступним чином: typeof a !== 'undefined' && Boolean(a)

Що таке замикання в JS, і як/для чого його використовують?

Замикання - прийом, при якому функція має доступ до лексичної області видимості своєї функції-обгортки, яка була виконана раніше, але не може бути звільнена з пам'яті через те, що її власна область видимості ще використовується іншою функцією. Краще за все це проілюструє даний приклад:

function counterFn() { // функція-обгортка
var count = 0; // змінна в області видимості функції обгортки

return function() { // функція, яка буде використовуватись у подальшому
return ++count; // посилання на змінну з області видимості функції-обгортки
}
}

const counter = counterFn(); // функція-обгортка виконана
console.log(counter()); // 1 використовуємо вкладену функцію
// стан лічильника зберігається між викликами, оскільки воно зберігається в області видимості виконаної функції-обгортки
console.log(counter()); // 2
console.log(counter()); // 3

Які конструкції мови ви використовуєте для ітерації властивостей об'єкта та елементів массиву?

Для перебору всіх (власних та успадкованих) властивостей об'єкта використовується цикл for..in. Для простого перебору елементів массиву частіше за все використовується функція Array.forEach()

Опишіть основні відмінності між методами Array.forEach() та Array.map(), в яких випадках ви б застосували кожен з них?

Метод Array.forEach() перебирає всі элементи массива і для кожного викликає передану в forEach callback функцію. Після відпрацювання нічого не повертає, при зміні элемента массива в callback функції зміни відображуються в вихідному масиві. Array.map() перебирає всі элементи массиву і для кожного викликає передану в forEach callback функцію. Після відпрацювання повертає новий массив, рівний по довжині вихідному, який містить в собі перетворені элементи массиву. При зміні элемента массива в callback функції зміни не відображуються у вихідому массиві.

Для чого використовують анонимні функції в JS ?

Анонімними називаются функції, які не мають власного імені, як наслідок, їх не можна спочатку оголосити, а потімм викликати. Найчастіше такі функції використовують в якості callback функцій.

В чому полягає відмінність між host objects і native objects ?

Native objects — об'єкти, визначені спецификацією ECMAScript, наприклад, Object (constructor), Date, Math. Host objects — об'єкти, чия роль полягає у створенні середовища виконання для ECMAScript, наприклад, window, document, location, history.

У чому полягає відмінність між Function Declaration та Function Expression в JavaScript?

Function Declaration (функція, оголошена у потоці коду) — классична форма оголошення функції.


function sum(a, b) {
return a + b;
}


Function Expression (функціональний вираз) — альтернативний синтаксис для оголошення функції.


var sum = function(a, b) {
return a + b;
}


По суті, вони роблять те саме, але функції, оголошені як Function Declaration можуть бути викликані раніше, ніж були оголошені у коді (hoisting), а Function Expression ні.


sum(2, 4);

function sum(a, b) {
return a + b;
}

Опишіть, що робить Function.call і Function.apply. В чому полягає основна відмінність між ними?

Обидва методи використовуються для вказання контексту, при виклику функції, до якої застосовується. Відрізняються синтаксисом, apply більш гнучка.

За що відповідає Function.prototype.bind ?

Function.prototype.bind — створює нову функцію, яка при виклику встановлює як контекст виконання (this) надане значення, тобто відповідає за виклик функцій з іншим контекстом.

За що відповідають і в чому полягає різниця між feature detection, feature inference та User Agent String?

Feature detection, feature inference та User Agent String – це практики визначення, чи існує певна функція web-технології у браузері.
Feature detection – це спосіб визначити, чи існує функція у певних браузерах.
Feature inference - припущення: якщо одна функція присутня (або ні), відповідно інша теж буде (або ні).
User Agent String - це текстовий рядок, який відправляє кожен браузер і до якого можна отримати доступ через navigator.userAgent. Цей рядок містить у собі інформацію про виконавче оточення.

Що таке підняття (hoisting) у JavaScript?

Під час компіляції коду, оголошення деяких змінних і функцій підіймаються вище за інший код у межах своєї області видимості. Цей процес і називається hoisting. Завдяки чому функція буде успішно викликана не зважаючи на те, що в коді її виклик може йти перед оголошенням.

Що таке спливання та занурення подій, у чому різниця між ними?

Сплив і занурення - це фази життєвого циклу події. Різниця полягає у моменті визначення факту настання події. Спочатку, при взаємодії користувача з елементом інтерфейсу (клік на кнопку, наприклад) подія занурюється від об'єкта window до цільового елементу (target), потім настає стадія спливання і подія спливає від target назад до window. Так, одна й та сама подія може бути перехоплена раніше чи пізніше.

Які плюси та мінуси розширення вбудованих об'єктів JavaScript?

Плюсом цього підходу є розширення базового функціоналу об'єкта. Прийом може бути застосований для визначення поліфілів. В цілому, розширення поведінки вбудованих об'єктів не вітається і є поганою практикою (monkey patching). Це порушує принцип інкапсуляції та засмічує базові об'єкти незадокументованою функціональністю.

Опишіть різницю між == і === в JS?

Обидва оператори порівняння перевіряють тотожність. Відмінність полягає в тому, що подвійне дорівнює при порівнянні значень неявно наводить (перетворює) типи значень до єдиного, так рядок "1" і цифра 1 за такого порівняння будуть рівні. Потрійне дорівнює не виконує жодних неявних трансформацій, а отже вихідні типи матимуть значення. Таким чином рядок не буде дорівнювати числу і не важливо, що в обох операндах фігурує одиниця.

Опишіть політику крос-доменних обмежень (same-origin policy) у тих JS?

Same-origin policy (політика однакового джерела) - визначає як сайт (документ/скрипт), завантажений з одного джерела, може взаємодіяти в браузері з ресурсом (файлом, скриптом, об'єктом), з іншого джерела. Приклад: Якщо скрипт нашого сайту робить простий запит (GET) до чужого сервера й отримує у відповідь файл, при цьому у відповіді відсутній заголовок Access-Control-Allow-Origin або у значенні цього заголовка відсутній наш домен, браузер заборонить доступ до цього файлу (не дасть переглянути вміст відповіді) зі скриптів нашого сайту.

Що таке Тернарний оператор? Про що говорить слово “тернарний” ?

Тернарний оператор - це умовний оператор, що має форму запису: умова ? істинний вираз : хибний вираз. Тернарним його називають тому, що він має три операнди.

Що таке strict режим у JS? У чому його переваги/недоліки?

Strict (суворий режим) — особливий режим роботи компілятора, що включає нові можливості та деякі поліпшення обумовлені стандартом ECMAScript 5, при якому змінюється поведінка деяких функцій. Вмикається директивою use strict. Більшість сучасних браузерів підтримують strict режим, однак в деяких не повністю. Також не варто забувати про старіші версії (IE нижче версії 10). Суворий режим змінює семантику, що призводить до похибок та помилок. Потрібно підходити дуже уважно до використання суворого режиму та проводити тести для перевірки працездатності коду як у браузерах, які підтримують суворий режим, так і тих, що не підтримують.

Які основні переваги / недоліки написання коду мовою, що компілюється в JavaScript?

TypeScript - одна з мов, яка дозволяє писати код, що компілюється в JS. Серед його переваг варто зазначити:

Підтримка популярних IDE: Sublime Text, Visual Studio Code, WebStorm, Eclipse.
Реалізує багато принципів ООП: модифікатори доступу, успадкування, інкапсуляцію та поліморфізм.
Дозволяє швидше та простіше писати складні рішення, які легше тестувати та розвивати завдяки підтримці ООП та суворій типізації.
Існує система для роботи з модулями, класами. Навіть є можливість створювати абстрактні класи.
TypeScript назад сумісний із JavaScript. Будь-який код, написаний на JS буде виконано. Також можна писати змішаний код і він буде валідним.

Недоліки:

Наявність додаткових файлів (*.ts, *.d.ts, *.map), що є незручним для невеликих проектів.
Для деяких браузерів потрібне додаткове налаштування консолі для налагодження TypeScript.
TypeScript - мова з неявною статичною типізацією: тип може бути описаний як any, що відключить приведення до цього типу змінної.
d.ts декларації не завжди відповідають поточній версії бібліотеки.

У цілому нині — TypeScript відмінний вибір великих проектів, оскільки написання займає більше часу через описи декларацій класів і методів. Без статичної типізації в JavaScript, TypeScript — відмінне рішення.

У чому різниця між mutable та immutable об'єктами? Наведіть приклад immutable об'єкта в JS.

Immutable об'єкт - незмінний. Об'єкт, стан якого не може бути змінено після створення. Відповідно, mutable об'єкт може бути змінений після створення. Усі примітиви (числа, рядки, булеві значення тощо) є імутабельними.
Переваги незмінності:

Просте та швидке відстеження змін (наприклад, не потрібно окремо порівнювати значення кожного поля вкладеного об'єкта. Можна просто порівняти посилання на об'єкти та відсіяти вкладені гілки порівнянь).
Більш безпечне використання та тестування.

Недоліки:

Створення нового об'єкта за кожної зміни може стати ресурсно-затратною операцією.

Як ви можете досягти незмінності (immutability) у власному коді?

Використання методу Object.freeze() запобігає зміні об'єкта: додавання, видалення або зміна властивостей. Використання ключового слова const замість var або let не робить константу незмінною, проте запобігає повторному присвоюванню нового значення.

Опишіть різницю між синхронними та асинхронними функціями

Синхронні функції - виконуються в порядку, в якому вони написані в тексті, по черзі.
Асинхронні функції - виконуються відкладено, потрапляючи перед виконанням у чергу, що дозволяє виконати їх, не блокуючи основний потік.

Що таке цикл подій (event loop) у JS? Яка різниця між «стеком викликів» (call stack) та «чергою завдань» (task queue)?

Task queue - черга із завершених асинхронних завдань, готових до синхронної обробки. З цієї черги завдання будуть поступово переміщені до call stack.
Call Stack — стек викликів за яким можна визначити, в якому місці програми зараз йде її обробка. Якщо стек не порожній, це означає виконання синхронних завдань, а саме, функції, яка знаходиться на верхівці стека.
Event loop — механізм, що переміщує готові до синхронної обробки асинхронні завдання з task queue в call stack.

У чому полягає різниця між змінними, створеними з допомогою let, var чи const?

const — змінним, оголошеним цим оператором, має бути одразу надано значення. Надалі значення може бути перевизначено. Змінна, оголошена в такий спосіб, не буде видно за межами блокової області видимості.
let — значення змінної, оголошеної цим оператором, може бути встановлене пізніше, а також може бути перевизначено в майбутньому. Змінну, оголошену в такий спосіб, не буде видно за межами блокової області видимості. Не доступний hoisting. Не підтримує повторне оголошення.

var — значення змінної, оголошеної цим оператором, може бути встановлене пізніше, а так само може бути перевизначене в майбутньому. Змінна, оголошена у такий спосіб, видно за межами блокової області видимості. Доступний hoisting. Підтримує повторне оголошення.

У чому різниця між класом ES6 і конструктором функцій ES5?

Методи класу є неперелічуваними властивостями; Код усередині класу за замовчуванням обробляється у строгому режимі; Є розбіжності у синтаксисі;

Чи можете ви назвати випадок у якому найбільш доречне застосування стрілкової функції? Чим цей тип функцій відрізняється від інших?

Стрілкові функції оголошуються особливою комбінацією символів () => {}. Мабуть, найбільш доречним варіантом застосування стрілкової функції є застосування її як callback функції. Це пояснюється основною відмінністю її від інших типів функцій - контекст виконання стрілкових функцій визначається не в момент виклику, а в момент оголошення (лексичний this).

Що таке функція вищого порядку?

Функція вищого порядку - функція яка приймає як аргумент іншу функцію або повертає функцію, тобто працює з іншими функціями. Array.prototype.reduce, Array.prototype.map та Array.prototype.filter – це функції вищого порядку.

Наведіть приклад деструктуруючого присвоєння (destructuring assignment) об'єкта чи масиву

Destructuring assignment - механізм вилучення даних з масивів та об'єктів. При цьому використовуються літерали масиву або об'єкта (залежно від того, що розуміємо), що збільшує читабельність і дозволяє відразу сказати об'єкт якого типу деструктурується. Наприклад:

var [firstItem] = arr;

По квадратних дужках відразу видно, що arr це масив

Наведіть приклад генерації рядка за допомогою шаблонного рядка ES6.

Шаблонний рядок - рядковий літерал, що дозволяє використовувати вирази всередині рядка. Оголошується косими лапками.
const variable = 'World!'
console.log(`Hello ${variable}`);

Що таке каррування в JS?

Каррування (currying) - перетворення функцій з безліччю аргументів на набір вкладених функцій з одним аргументом. Після виклику такої функції з передачею їй аргументу, вона повертає нову функцію, яка чекає на наступний аргумент і так до отримання результату.

Які переваги використання spread syntax і чим він відрізняється від rest syntax?

Spread — оператор, що складається з трьох точок. Перевага – у короткій формі запису. Відмінність — залежно від місця застосування цей оператор сприймається як spread чи rest оператор. Rest використовується для деструктуризації колекцій (поділ на окремі елементи), а spread, навпаки, для з'єднання окремих значень масиву.

let arr = ['one', 'two', 'three', 'four'];
// деструктуризація трьома точками (rest)
let [first, second, ...rest] = arr;
console.log(first); // 'one'
console.log(second); // 'two'
console.log(rest); // ['three', 'four']
// об'єднання трьома точками. Схожий синтаксис, але поведінка інша
const restored = [first, second, ...rest];
console.log(restored) //['one', 'two', 'three', 'four'];