Partial: примеры (JAVASCRIPT)
partial(fn (function), ...args (any)): functionЧто такое функция partial
В JavaScript функция partial сама по себе не является встроенной. Это программный шаблон, часто реализуемый разработчиками для частичного применения другой функции. При частичном применении создается новая функция, где часть аргументов оригинальной функции фиксируется заранее.
Эта техника используется для:
- Упрощения вызова функций с повторяющимися аргументами.
- Создания специализированных функций из более общих.
- Подготовки функций, соответствующих ожидаемой сигнатуре (например, для передачи в
mapилиsetTimeout).
Аргументы реализации partial:
- func: Исходная функция, к которой применяется частичное применение (обязательный).
- presetArgs: Массив или список аргументов, которые фиксируются первыми (опционально).
- context: Значение
this, которое будет привязано к новой функции (часто реализуется отдельно вbind).
Возвращаемое значение: Новая функция, которая при вызове объединит заранее фиксированные аргументы с переданными в момент вызова и выполнит исходную функцию func.
Базовые примеры применения
Пример реализации и использования простой функции partial.
function partial(fn, ...presetArgs) {
return function(...laterArgs) {
return fn.apply(this, [...presetArgs, ...laterArgs]);
};
}
// Исходная функция
function greet(greeting, name) {
return `${greeting}, ${name}!`;
}
// Создание частично примененной функции
const sayHello = partial(greet, "Hello");
const sayHi = partial(greet, "Hi");
console.log(sayHello("Alice"));
console.log(sayHi("Bob"));Hello, Alice! Hi, Bob!
Пример с указанием контекста выполнения.
function multiply(a, b) {
return a * b;
}
// Фиксация первого аргумента
const double = partial(multiply, 2);
const triple = partial(multiply, 3);
console.log(double(7));
console.log(triple(5));14 15
Похожие методы в JavaScript
- Function.prototype.bind(): Встроенный метод для создания новой функции с привязанным контекстом
thisи начальными аргументами. Предпочтительнее, когда важен контекст или нужна точная встроенная реализация. - Каррирование (currying): Процесс преобразования функции от нескольких аргументов в цепочку функций, каждая от одного аргумента. Каррирование и частичное применение — родственные, но разные техники. Каррированная функция ожидает аргументы по одному.
- Замыкания вручную: Создание функции-обёртки, захватывающей нужные аргументы через замыкание. Используется для полного контроля над поведением.
Аналоги в других языках
Python: functools.partial
from functools import partial
def greet(greeting, name):
return f"{greeting}, {name}!"
say_hello = partial(greet, "Hello")
print(say_hello("Alice")) # Hello, Alice!Hello, Alice!
PHP: Замыкания (Closure)
function partial($fn, ...$presetArgs) {
return function(...$laterArgs) use ($fn, $presetArgs) {
return $fn(...array_merge($presetArgs, $laterArgs));
};
}
$greet = function($greeting, $name) { return "$greeting, $name!"; };
$sayHello = partial($greet, "Hello");
echo $sayHello("Alice");Hello, Alice!
C++: std::bind (очень упрощенно)
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
void greet(string greeting, string name) {
cout << greeting << ", " << name << "!" << endl;
}
int main() {
auto sayHello = bind(greet, "Hello", _1);
sayHello("Alice"); // Hello, Alice!
return 0;
}Распространенные ошибки
1. Потеря контекста this при использовании самописной partial без явной передачи.
const obj = {
prefix: 'Mr.',
getName: function(first, last) {
return `${this.prefix} ${first} ${last}`;
}
};
const badPartial = partial(obj.getName, 'John');
console.log(badPartial('Doe')); // undefined John Doeundefined John Doe
2. Путаница между partial и каррированием.
// Каррированная версия (ожидает один аргумент за раз)
function curriedAdd(a) {
return function(b) {
return a + b;
};
}
// Частичное применение (может зафиксировать несколько сразу)
const addFive = partial((a, b) => a + b, 5);
// curriedAdd(5)(10) vs addFive(10)Изменения в реализации
Поскольку partial не является нативной функцией JavaScript, её изменения зависят от реализации в библиотеках или пользовательском коде. В современных версиях ES6+ типичные реализации стали лаконичнее благодаря использованию операторов rest/spread и стрелочных функций.
Пример эволюции реализации:
// Старый стиль (ES5)
function partialES5(fn) {
var presetArgs = Array.prototype.slice.call(arguments, 1);
return function() {
var laterArgs = Array.prototype.slice.call(arguments);
return fn.apply(this, presetArgs.concat(laterArgs));
};
}
// Современный стиль (ES6+)
const partial = (fn, ...presetArgs) => (...laterArgs) => fn(...presetArgs, ...laterArgs);Расширенные примеры использования
1. Частичное применение с пропускаемыми аргументами (используя плейсхолдер, например, _).
function partialWithPlaceholders(fn, ...args) {
const placeholders = args.map(arg => arg === partialWithPlaceholders._ ? null : arg);
return function(...laterArgs) {
let i = 0;
const finalArgs = placeholders.map(arg =>
arg === null ? laterArgs[i++] : arg
);
return fn(...finalArgs, ...laterArgs.slice(i));
};
}
partialWithPlaceholders._ = Symbol('_');
function log(a, b, c, d) {
console.log(a, b, c, d);
}
const logWithSkip = partialWithPlaceholders(log, 'A', partialWithPlaceholders._, 'C');
logWithSkip('B', 'D'); // Заполняет пропуск вторым аргументомA B C D
2. Композиция функций с partial.
const compose = (f, g) => x => f(g(x));
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const addTwo = partial(add, 2);
const triple = partial(multiply, 3);
const addTwoThenTriple = compose(triple, addTwo);
console.log(addTwoThenTriple(5)); // (5 + 2) * 321
3. Использование в обработчиках событий или колбэках.
function createLogger(moduleName, message) {
console.log(`[${moduleName}] ${message}`);
}
const logModuleA = partial(createLogger, 'ModuleA');
const logModuleB = partial(createLogger, 'ModuleB');
// Передача в setTimeout
setTimeout(partial(logModuleA, 'Загрузка завершена'), 1000);
// Использование в обработчике
// button.addEventListener('click', partial(logModuleB, 'Клик'));4. Частичное применение для методов объекта с фиксацией контекста.
function bindPartial(context, methodName, ...presetArgs) {
return context[methodName].bind(context, ...presetArgs);
}
const calculator = {
multiply(a, b) { return a * b; }
};
const double = bindPartial(calculator, 'multiply', 2);
console.log(double(10)); // 2020