Junior: питання на співбесіді з теми «Сучасний синтаксис»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
Деструктуризація - короткий запис, щоб витягти значення з об'єкта чи масиву в окремі змінні.
Об'єкти - за назвами властивостей:
const user = { id: 7, name: 'Оля', city: 'Львів', role: 'admin' };
const { name, city } = user; // name = 'Оля', city = 'Львів'
const { name: userName } = user; // перейменування
const { phone = 'не вказано' } = user; // значення за замовчуванням
const { id, ...profile } = user; // решта властивостей в об'єкт
Масиви - за позицією:
const [first, second] = ['php', 'laravel', 'vue'];
const [, , third] = ['php', 'laravel', 'vue']; // пропуск елементів
const [head, ...tail] = [1, 2, 3, 4]; // tail = [2, 3, 4]
let a = 1, b = 2;
[a, b] = [b, a]; // обмін значеннями
Вкладена деструктуризація:
const { address: { city } } = order; // city з order.address
Якщо order.address - undefined, буде TypeError. Захист - значення за замовчуванням: const { address: { city } = {} } = order;.
Де це використовується найчастіше:
- параметри функцій - іменовані опції:
function createUser({ name, email, role = 'user' }) { /* ... */ }
createUser({ email: 'a@b.ua', name: 'Оля' });
- імпорти:
import { ref, computed } from 'vue'- схожий синтаксис; - результат функції з кількома значеннями:
const [count, setCount] = useState(0)у React,const { data, error } = await request(); - перебір пар:
for (const [key, value] of Object.entries(prices)); - відповідь API:
const { data, meta: { total } } = await response.json();.
Що варто знати:
- значення за замовчуванням спрацьовує лише для
undefined, не дляnull:const { limit = 10 } = { limit: null }дастьnull; - деструктуризувати
nullчиundefinedнеможливо -TypeError; - деструктуризація масиву працює з будь-яким ітерованим значенням: рядком,
Set,Map(const [[key, value]] = map); - деструктуризація створює поверхневі копії значень: вкладені об'єкти лишаються спільними з оригіналом.
Аналог у PHP - [$first, $second] = $array і ['name' => $name] = $user.
Три крапки ... означають протилежні речі залежно від місця:
- spread (розгортання) - «розкласти» масив чи об'єкт на окремі елементи. Стоїть там, де використовують значення;
- rest (решта) - «зібрати» кілька елементів в один масив чи об'єкт. Стоїть там, де оголошують змінні чи параметри.
Spread:
// масиви
const all = [...frontend, ...backend]; // об'єднання
const copy = [...items]; // поверхнева копія
Math.max(...prices); // масив як аргументи функції
const unique = [...new Set(tags)]; // будь-який ітерований об'єкт у масив
// об'єкти
const defaults = { perPage: 20, sort: 'name' };
const options = { ...defaults, ...userOptions }; // пізніші властивості перекривають ранніші
const updated = { ...user, name: 'Іра' }; // копія зі зміненим полем
Rest:
function log(level, ...messages) {} // решта аргументів у масив
const [first, ...others] = list; // решта елементів
const { password, ...safeUser } = user; // усе, крім password
Останній приклад - зручний спосіб прибрати поле з об'єкта без мутації.
Що варто знати про spread:
- копія поверхнева. Вкладені об'єкти й масиви лишаються спільними:
const copy = { ...settings };
copy.limits.upload = 1; // змінило й settings.limits
Для глибокої копії - structuredClone(settings).
- порядок важливий:
{ ...user, role: 'admin' }перезапише роль, а{ role: 'admin', ...user }- ні, якщо вuserвона є; - spread об'єкта копіює лише власні перелічувані властивості - без прототипу, гетери обчислюються в значення. Тому
{ ...dateInstance }чи{ ...classInstance }дасть простий об'єкт без методів; - spread
nullчиundefinedв об'єкті нічого не додає (зручно для умовних полів:{ ...(isAdmin && { role: 'admin' }) }), а в масиві - кидаєTypeError; - дуже великі масиви як аргументи (
Math.max(...hugeArray)на сотні тисяч елементів) можуть перевищити ліміт аргументів - для нихreduce.
Spread - основа незмінних оновлень стану у React, Pinia, Redux: замість зміни об'єкта створюється новий з потрібними змінами.
У PHP ті самі три крапки: ...$args у параметрах (rest) і [...$a, ...$b] чи f(...$args) (spread).
Шаблонні рядки записують у зворотних лапках `. Вони підтримують підстановку виразів і багаторядковість:
const name = 'Оля';
const count = 3;
const message = `Привіт, ${name}! У вас ${count} нових ${count === 1 ? 'повідомлення' : 'повідомлень'}.`;
const html = `
<div class="card">
<h3>${title}</h3>
</div>
`;
Усередині ${...} - будь-який вираз: виклик функції, тернарний оператор, арифметика. Результат перетворюється на рядок.
Чим кращі за склеювання через +:
- читається як готовий текст, без нагромадження лапок і плюсів;
- переноси рядків зберігаються як є;
- не треба екранувати одинарні й подвійні лапки всередині.
Пастки:
- HTML з даними користувача через шаблонний рядок і
innerHTML- XSS. Шаблонний рядок нічого не екранує; - відступи багаторядкового шаблону потрапляють у рядок;
undefinedіnullпідставляться як текст'undefined'/'null'- видно в інтерфейсі, якщо дані не прийшли.
Теговані шаблони - функція перед шаблоном отримує окремо статичні частини й підставлені значення:
function highlight(strings, ...values) {
return strings.reduce(
(result, part, i) => result + part + (i < values.length ? `<mark>${escapeHtml(values[i])}</mark>` : ''),
'',
);
}
highlight`Знайдено ${count} результатів для «${query}»`;
Функція бачить, де закінчується довірений текст розробника і починаються дані, - і може їх обробити: екранувати, параметризувати.
Де теговані шаблони використовують:
- безпечний SQL: бібліотеки на кшталт
postgres(sql`SELECT * FROM users WHERE id = ${id}`) перетворюють значення на параметри запиту, а не вставляють у текст - захист від SQL-ін'єкцій; - HTML-шаблони з екрануванням:
lit-html(html`<p>${text}</p>`); - CSS-in-JS: styled-components;
- GraphQL-запити:
gql`...`; String.raw- вбудований тег, що не обробляє екранування:String.raw`C:\new\path`лишає\nяк два символи. Зручно для регулярних виразів і шляхів Windows.
Масив статичних частин має властивість raw - ті самі частини без обробки екранування.
Клас - синтаксис для створення об'єктів з однаковою структурою й поведінкою. Під капотом це звичайна функція-конструктор з прототипом, але запис читабельніший.
class Product {
static count = 0; // статичне поле: належить класу, а не екземпляру
currency = 'UAH'; // поле екземпляра зі значенням за замовчуванням
#discount = 0; // приватне поле
constructor(name, price) {
this.name = name;
this.price = price;
Product.count++;
}
get finalPrice() { // гетер: читається як властивість
return this.price - this.#discount;
}
applyDiscount(amount) { // метод: спільний для всіх екземплярів через прототип
this.#discount = amount;
return this;
}
static fromJson(data) { // статичний метод: фабрика
return new Product(data.name, data.price);
}
}
const p = new Product('Книга', 500).applyDiscount(50);
p.finalPrice; // 450
Product.count; // 1
Успадкування:
class DigitalProduct extends Product {
constructor(name, price, url) {
super(name, price); // обов'язково до звернення до this
this.url = url;
}
toString() {
return `${this.name} (${this.url})`;
}
}
Правила, на яких часто помиляються:
super()передthisу конструкторі класу-нащадка - інакшеReferenceError: Must call super constructor ... before accessing 'this';- клас без
newвикликати не можна:Product('x')даєTypeError: Class constructor Product cannot be invoked without 'new'; - класи не піднімаються як функції: використати клас до його оголошення -
ReferenceError(тимчасова мертва зона, як уlet); - тіло класу завжди в суворому режимі;
- методи не прив'язані до екземпляра:
const fn = p.applyDiscount; fn(10)втратитьthis. Для обробників подій використовують стрілкову функцію в полі класу чиbind.
Поля чи присвоєння в конструкторі: поля класу оголошують структуру об'єкта в одному місці й створюються до виконання тіла конструктора (для нащадка - одразу після super()).
Статичний блок static { ... } виконується один раз при оголошенні класу - для складної ініціалізації статичних полів.
Коли клас доречний: об'єкти з власним станом і поведінкою, ієрархії, з якими працює бібліотека (веб-компоненти, класи помилок). Для простих даних достатньо об'єктного літерала, а для поведінки без стану - звичайних функцій.