Функція-фабрика
Функція-фабрика (function factory) це функція, яка повертає інші функції. Під час виклику вона створює і віддає нову функцію з поведінкою, що залежить від переданих аргументів, тому її й називають фабрикою.
Теорія
TL;DR
- Фабрика це функція, результатом якої є інша функція.
- Побудована на closure: створена функція пам'ятає аргументи фабрики.
- Кожен виклик фабрики дає незалежний екземпляр з власним станом.
- Основні вигоди: перевикористання логіки, інкапсуляція даних, гнучкість.
- Типові приклади: лічильники, фільтри, логери, часткове застосування (partial application).
Швидкий приклад
function createGreeter(greeting) {
return function (name) {
console.log(`${greeting}, ${name}!`);
};
}
const sayHi = createGreeter('Hello');
const sayBye = createGreeter('Goodbye');
sayHi('Maria'); // Hello, Maria!
sayBye('Oleh'); // Goodbye, Oleh!Тут createGreeter це фабрика, а sayHi і sayBye це функції, «вироблені» нею. Кожна з них замикає своє значення greeting.
Власний стан через closure
Найцінніша властивість фабрики: кожен її виклик створює окреме оточення змінних, до якого має доступ лише повернута функція.
function createCounter(start = 0) {
let count = start;
return function () {
count++;
return count;
};
}
const counterA = createCounter();
const counterB = createCounter(10);
console.log(counterA()); // 1
console.log(counterA()); // 2
console.log(counterB()); // 11counterA і counterB не заважають одне одному: у кожного свій count, і ззовні до нього не дістатися інакше, ніж через повернуту функцію. Це і є інкапсуляція без класів і без this.
Конфігурація поведінки: фільтри
Фабрика дозволяє винести в параметр те, що змінюється, і не дублювати решту коду.
function createFilter(minValue) {
return function (numbers) {
return numbers.filter(n => n >= minValue);
};
}
const filterAbove10 = createFilter(10);
console.log(filterAbove10([5, 8, 13, 21])); // [13, 21]Часткове застосування (partial application)
Коли фабрика фіксує частину аргументів майбутнього виклику, отримуємо часткове застосування, близький родич каррінгу (currying).
function multiplyBy(factor) {
return function (number) {
return number * factor;
};
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15Це зручний спосіб створювати спеціалізовані функції «на льоту» і передавати їх у map, filter чи обробники подій.
Реальний випадок: логер
function createLogger(prefix) {
return function (message) {
console.log(`[${prefix}] ${message}`);
};
}
const info = createLogger('INFO');
const error = createLogger('ERROR');
info('Server started'); // [INFO] Server started
error('Something went wrong'); // [ERROR] Something went wrongОдин шаблон, різні функції для різних контекстів.
Чому це зручно
| Перевага | Опис |
|---|---|
| Перевикористання коду | Фабрика створює багато схожих функцій без дублювання логіки |
| Інкапсуляція даних | Використовує closure, щоб зберігати внутрішній стан |
| Гнучкість | Можна створювати функції з динамічною поведінкою |
| Функціональний стиль | Добре поєднується з map, filter, reduce |
Підсумкова таблиця
| Властивість | Опис |
|---|---|
| Що це | Функція, яка повертає іншу функцію |
| Основна ідея | «Генерувати» функції за спільним шаблоном |
| На чому базується | Closure |
| Де застосовується | Конфігурації, обробники, логери, лічильники, фабричні патерни |
| Переваги | Менше повторів, більше гнучкості та читабельності |
Типові помилки
- Плутати функцію-фабрику з фабрикою об'єктів: перша повертає функцію, друга повертає об'єкт (хоча обидві спираються на ту саму ідею).
- Виносити стан за межі фабрики, наприклад у змінну модуля. Тоді всі «вироблені» функції ділять один лічильник і незалежність зникає.
- Створювати фабрику всередині рендера чи циклу без потреби: щоразу народжується нова функція, а це зайві алокації та зламана мемоізація в UI-фреймворках.
- Забувати, що closure тримає посилання на всі захоплені змінні. Якщо фабрика захопила великий об'єкт, він не звільниться, поки жива повернута функція.
- Робити фабрику з трьох рівнів вкладеності там, де вистачило б одного аргументу: код стає важчим для читання без жодної вигоди.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.