JavaScript · Синтаксис · Проміжний

Стрілочні функції та this

10 завдань

Опануй синтаксис стрілочних функцій та зрозумій як `this` поводиться по-різному у стрілочних і звичайних функціях — ключова відмінність для класів, зворотних викликів та обробників подій.

Синтаксис стрілочних функцій та як працює this

#
Стрілочні функції — це компактна альтернатива звичайним виразам функцій. Різниця в синтаксисі проста; різниця в поведінці `this` — фундаментальна. **Синтаксис** ```javascript // Звичайний вираз функції const double = function(x) { return x * 2; }; // Стрілочна функція — те саме const double = (x) => { return x * 2; }; // Один параметр — дужки необов'язкові const double = x => { return x * 2; }; // Один вираз — фігурні дужки та return необов'язкові (неявне повернення) const double = x => x * 2; // Без параметрів — дужки обов'язкові const greet = () => "Hello!"; // Повернення літерала об'єкта — обгорни в дужки щоб уникнути неоднозначності з блоком const makeUser = name => ({ name: name, active: true }); ``` **Як `this` працює у стрілочних функціях** Звичайна функція отримує власний `this` — визначається тим як вона викликається. Стрілочна функція не має власного `this`. Вона захоплює `this` з навколишнього лексичного середовища в момент визначення стрілочної функції, і він ніколи не змінюється. ```javascript class Timer { constructor() { this.seconds = 0; } start() { // Стрілочна функція: this === екземпляр Timer (лексичний this) setInterval(() => { this.seconds++; console.log(this.seconds); }, 1000); } startBroken() { // Звичайна функція: this === undefined (strict) або глобальний об'єкт setInterval(function() { this.seconds++; // TypeError або NaN — неправильний this }, 1000); } } ``` **Коли НЕ використовувати стрілочні функції** Стрілочні функції підходять не завжди: - Методи об'єкта: `this` у стрілочному методі посилається на зовнішню область, не на об'єкт. - Конструктори: стрілочні функції не можна викликати через `new` — вони кидають TypeError. - Об'єкт `arguments`: у стрілочних функцій немає власного `arguments`. - Динамічний `this` (обробники подій де `this` має бути елементом): використовуй звичайні функції. ```javascript const obj = { value: 42, getValue: () => this.value, // неправильно — this не є obj getValueOk() { return this.value; }, // правильно }; ```

Стрілочні функції у методах масиву, класах та промісах

#
Стрілочні функції особливо зручні в трьох контекстах: методи масивів, методи класів з асинхронними колбеками та ланцюжки промісів. Кожен випадок використовує лексичний `this` або лаконічний синтаксис. **Методи масивів — найпоширеніше використання** ```javascript const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10] const evens = numbers.filter(n => n % 2 === 0); // [2, 4] const total = numbers.reduce((sum, n) => sum + n, 0); // 15 // Ланцюжок const result = numbers .filter(n => n > 2) .map(n => n * 10); // [30, 40, 50] ``` Неявне повернення робить колбеки з одним виразом лаконічними без втрати ясності. **Методи класів з setTimeout / setInterval** ```javascript class Counter { constructor() { this.count = 0; } startCounting() { setInterval(() => { this.count++; console.log(this.count); }, 1000); } } ``` Стрілочна функція всередині `setInterval` захоплює `this` з області `startCounting` — тобто з екземпляра класу. Звичайна функція втратила б `this` при виклику таймером. **Ланцюжки промісів** ```javascript fetch("/api/user") .then(res => res.json()) .then(data => { console.log(data.name); return data; }) .catch(err => console.error(err)); ``` Стрілочні функції роблять ланцюжок читабельним. Кожен колбек `.then` — однорядковий або короткий блок без зайвого ключового слова `function`. **Повернення об'єкта з неявним return** ```javascript const makePoint = (x, y) => ({ x, y }); makePoint(3, 5); // { x: 3, y: 5 } ``` Обгортай об'єкт у дужки — інакше `{` сприймається як початок блоку, не об'єктний літерал, і функція повертає `undefined`.

Стрілочні vs звичайні функції — довідник

#
Порівняння пліч-о-пліч та швидкий довідник синтаксису. **Варіанти синтаксису** ```javascript // Кілька параметрів, блок тіло, явне повернення const add = (a, b) => { return a + b; }; // Кілька параметрів, неявне повернення const add = (a, b) => a + b; // Один параметр, неявне повернення const double = x => x * 2; // Без параметрів const rand = () => Math.random(); // Повернення об'єктного літерала (обгорнути в дужки) const point = (x, y) => ({ x, y }); ``` **Стрілочна vs звичайна функція — ключові відмінності** `this`: Стрілочна — лексичний (успадковує з навколишньої області). Звичайна — динамічний (визначається місцем виклику). `new`: Стрілочна — заборонено (кидає TypeError). Звичайна — дозволено (створює екземпляр). `arguments`: Стрілочна — немає власного `arguments` (успадковує з функції що охоплює). Звичайна — має власний об'єкт `arguments`. `prototype`: Стрілочна — немає властивості `prototype`. Звичайна — є `prototype`. `super`: Стрілочна — успадковує від охоплюючого методу. Звичайна — кожен метод має власний `super`. **Коли використовувати кожну** Обирай стрілочну коли: - Колбеки для методів масиву (map, filter, reduce) - Колбеки де потрібно зберегти `this` з зовнішньої області (setTimeout, setInterval, проміси) - Короткі вбудовані вирази де `function` додає шум Обирай звичайну функцію коли: - Методи об'єкта (потрібен `this` як об'єкт) - Конструктори (виклик через `new`) - Функції що використовують `arguments` - Обробники подій де `this` має бути цільовим елементом
01

Переписати як стрілочну функцію

#

Перепиши функцію `greet` як стрілочну функцію, присвоєну до `const`. Поведінка має бути ідентичною.

function greet(name) {
    return "Hello, " + name + "!";
}

console.log(greet("Alice"));
Рішення
const greet = name => "Hello, " + name + "!";

console.log(greet("Alice"));
02

Неявне повернення — скоротити до одного рядка

#

Скороти `multiply` до однорядкової стрілочної функції з неявним поверненням.

const multiply = (a, b) => {
    return a * b;
};

console.log(multiply(3, 4));
Рішення
const multiply = (a, b) => a * b;

console.log(multiply(3, 4));
03

this у методі об'єкта — чому стрілка ламає

#

Метод `describe` використовує стрілочну функцію і повертає неправильний результат. Виправ його так щоб `this` правильно посилався на об'єкт.

const user = {
    name: "Alice",
    age: 30,
    describe: () => {
        return `${this.name} is ${this.age} years old`;
    }
};

console.log(user.describe());
Рішення
const user = {
    name: "Alice",
    age: 30,
    describe() {
        return `${this.name} is ${this.age} years old`;
    }
};

console.log(user.describe());
04

Виправити this у setTimeout колбеку

#

Клас `DelayedGreeter` має вивести привітання через 0мс, але `this.name` є undefined. Виправ за допомогою стрілочної функції.

class DelayedGreeter {
    constructor(name) {
        this.name = name;
    }

    greet() {
        setTimeout(function() {
            console.log("Hello, " + this.name + "!");
        }, 0);
    }
}

new DelayedGreeter("Alice").greet();
Рішення
class DelayedGreeter {
    constructor(name) {
        this.name = name;
    }

    greet() {
        setTimeout(() => {
            console.log("Hello, " + this.name + "!");
        }, 0);
    }
}

new DelayedGreeter("Alice").greet();
05

Стрілочні функції у map та filter

#

З масиву `products` відфільтруй лише товари з `price > 100` і перетвори кожен лише на `name`. Використовуй стрілочні функції для обох операцій. Виведи результат.

const products = [
    { name: "Laptop", price: 999 },
    { name: "Mouse", price: 25 },
    { name: "Monitor", price: 399 },
    { name: "Keyboard", price: 75 },
];
// твій код тут
Рішення
const products = [
    { name: "Laptop", price: 999 },
    { name: "Mouse", price: 25 },
    { name: "Monitor", price: 399 },
    { name: "Keyboard", price: 75 },
];
const result = products
    .filter(p => p.price > 100)
    .map(p => p.name);
console.log(result);
06

Повернути об'єкт зі стрілочної функції

#

Напиши стрілочну функцію `makeProduct`, яка приймає `name` і `price` та повертає об'єкт `{ name, price }` з неявним поверненням. Перевір з `"Laptop"` і `999`.

// твій код тут

console.log(makeProduct("Laptop", 999));
Рішення
const makeProduct = (name, price) => ({ name, price });

console.log(makeProduct("Laptop", 999));
07

Метод класу vs стрілочна властивість

#

Клас `Counter` має `increment` як звичайний метод і `decrement` як стрілочну функцію-властивість. Виклич обидва на екземплярі та виведи `count` після кожного. Спостерігай яка форма коректно працює після деструктуризації.

class Counter {
    count = 0;

    increment() {
        this.count++;
    }

    decrement = () => {
        this.count--;
    };
}

const c = new Counter();
c.increment();
console.log(c.count);
c.decrement();
console.log(c.count);

// Деструктуруй і виклич — яка ламається?
const { increment, decrement } = c;
increment();
console.log(c.count);
decrement();
console.log(c.count);
Рішення
class Counter {
    count = 0;

    increment() {
        this.count++;
    }

    decrement = () => {
        this.count--;
    };
}

const c = new Counter();
c.increment();
console.log(c.count);
c.decrement();
console.log(c.count);

const { increment, decrement } = c;
try { increment(); } catch(e) { console.log("increment failed:", e.message); }
console.log(c.count);
decrement();
console.log(c.count);
08

Карінг через вкладені стрілочні функції

#

Напиши каррованою функцію `add` зі стрілочними функціями: `add(a)(b)` має повертати `a + b`. Перевір через `add(3)(4)` та `add(10)(20)`.

// твій код тут

console.log(add(3)(4));
console.log(add(10)(20));
Рішення
const add = a => b => a + b;

console.log(add(3)(4));
console.log(add(10)(20));
09

Стрілочні функції у ланцюжку промісів

#

Завершити ланцюжок промісів: resolve з значенням `42`, потім подвоїти у `.then`, потім вивести результат у наступному `.then`. Використовуй стрілочні функції для обох колбеків.

const p = new Promise(resolve => {
    resolve(42);
});

p
    .then(/* подвоїти значення */)
    .then(/* вивести результат */);
Рішення
const p = new Promise(resolve => {
    resolve(42);
});

p
    .then(val => val * 2)
    .then(val => console.log(val));
10

Обрати правильний тип функції

#

Код нижче має два баги: один метод неправильно використовує стрілочну функцію, а один колбек неправильно використовує звичайну функцію. Виправ обидва.

class Playlist {
    constructor(name) {
        this.name = name;
        this.songs = [];
    }

    // Має виводити "Playlist: Rock"
    describe: () => {
        console.log("Playlist: " + this.name);
    }

    // Має виводити кожну пісню з назвою плейлиста
    printSongs() {
        this.songs.forEach(function(song) {
            console.log(this.name + ": " + song);
        });
    }
}

const pl = new Playlist("Rock");
pl.songs = ["AC/DC", "Led Zeppelin"];
pl.describe();
pl.printSongs();
Рішення
class Playlist {
    constructor(name) {
        this.name = name;
        this.songs = [];
    }

    describe() {
        console.log("Playlist: " + this.name);
    }

    printSongs() {
        this.songs.forEach(song => {
            console.log(this.name + ": " + song);
        });
    }
}

const pl = new Playlist("Rock");
pl.songs = ["AC/DC", "Led Zeppelin"];
pl.describe();
pl.printSongs();