Рассмотрим следующий пример:
const samples = ["foo", "bar"];
const excludeFoos = function(item) {
return item !== "foo";
}
const foos = samples.filter(excludeFoos);
Рассмотрим следующий пример:
const samples = ["foo", "bar"];
const excludeFoos = function(item) {
return item !== "foo";
}
const foos = samples.filter(excludeFoos);
Именование вещей
"Если у вас есть имя духа, у вас есть власть над ним". – Джеральд Джей Суссман
Можете ли вы придумать лучшее имя для вашей функции exclude
? Я знаю что могу. Он известен как notEqual
. Простое знание этого как своего истинного имени делает его гораздо более универсальным, когда дело касается решения проблем. "исключить" имеет смысл в контексте фильтрации массива, но почему-то это имеет меньшее значение, если мы хотим использовать функцию exclude
в другом месте.
if (exclude(a,b))
console.log("a and b are not equal")
Функциональное программирование состоит в том, чтобы сделать функции максимально возможными, так что, двигаясь вперед, давайте придерживаться
const notEqual = (x,y) => x !== y
Function.prototype.bind
Function.prototype.bind используется для привязки значений к параметрам функции. Он обычно используется, потому что он является родным, поскольку ECMAScript 5 – что означает, что вы можете достичь своей цели, не добавляя никаких дополнительных зависимостей или не внося никаких изменений в существующий код.
const notEqual = (x,y) => x !== y
const samples = ['foo', 'bar']
const foos = samples.filter(notEqual.bind(null, 'foo'))
console.log(foos) // ["bar"]
Вы можете использовать bind()
для создания новой функции с привязанными параметрами;
//you can replace the param with anything you like, null is for the context
var excludeFoos = exclude.bind(null,"foos")
const foos = samples.filter(excludeFoos);
С ES6:
const foos = samples.filter(x => exclude(x, "foos"));
другой вариант - использовать bind()
, но мне трудно читать:
const foos = samples.filter(exclude.bind(null, "foos"))
вы хотите выполнить свою функцию следующим образом: -
const samples = ["foo", "bar"];
const exclude = function(s) {
return item => item !== s;
}
const foos = samples.filter(exclude("foo"));
console.log(foos)
Здесь один для вас:
Есть несколько ответов, которые говорят о карри и частичном приложении.
И это отличное направление.
Но как только вы действительно получите функции более высокого порядка, вы можете сделать этот материал действительно чистым и легким в работе.
const curry = (f, ...initialArgs) => (...extraArgs) => {
const args = [...initialArgs, ...extraArgs];
return args.length >= f.length ? f(...args) : curry(f, ...args);
};
Так что это значит?
Он позволяет вам передать функцию и дает вам функцию. Пока вы не пройдете достаточно аргументов для запуска функции, она продолжит передавать вам другую функцию, которая ожидает больше аргументов.
Что хорошего в этом?
const multiply = curry((x, y) => x * y);
const double = multiply(2);
const triple = multiply(3);
double(2); // 4
triple(9); // 27
Теперь очень легко определить что-то вроде вашего теста.
const notEqual = curry((test, x) => test !== x);
// you could do it like this, to reuse `notFoo`
const notFoo = notEqual("foo");
samples.filter(notFoo);
// you could do it like this, if you don't need `notFoo`
samples.filter(notEqual("foo"));
Но подождите! Там больше!
const filter = curry((predicate, array) => array.filter(predicate));
const removeFoos = filter(notEqual("foo"));
removeFoos(samples);
removeFoos(items);
removeFoos(otherStuff);
Теперь у меня есть функция, которая отфильтровывает foos, и я могу просто передавать ее массивы всякий раз, когда мне кажется.
Последний на данный момент:
const compose = (...fs) => x => fs.reduceRight((x, f) => f(x), x);
Вместо записи
h(g(f(x)));
Compose позволяет мне писать
const hgf = compose(h, g, f);
hgf(x);
hgf(y);
hgf(z);
// it read from right to left
const tto = compose(three, two, one);
// or from bottom to top
const tsf = compose(
third,
second,
first
);
// because it runs like
y = third(second(first(x)));
Итак, давайте попробуем что-то дикое...
// lib functions (Ramda would work fine)
const map = curry((transform, array) => array.map(transform));
const reduce = curry((summarize, seed, array) =>
array.reduce(summarize, seed));
const flatMap = curry((transform, array) =>
array.map(transform).reduce((a, b) => a.concat(b), []));
// business functions
const castToEmployee = personData => new Employee(personData);
const isWorking = ({ active }) => active;
const removeSuperiors = curry((user, employee) =>
employee.role <= user.role);
const customEmployeeCriteria = (criteria, employee) => { /*...*/ };
const removeDuplicates = (arr, employee) =>
arr.some(person => person.id === employee.id)
? arr
: arr.concat(employee);
Код библиотеки
const performCustomSearch = searchCriteria =>
filter(cutomEmployeeCriteria(searchCriteria));
const getAuthorizedEmployeeList = currentUser =>
filter(removeSuperiors(currentUser));
const buildEmployees = compose(
filter(isWorking),
map(castToEmployee),
);
const cleanResults = compose(
filter(removeBrokenItem),
map(removePrivateMembers),
reduce(removeDuplicates, []),
);
const handleEmployeeRequest = (currentUser, searchCriteria) => compose(
cleanResults,
performCustomSearch(searchCriteria),
getAuthorizedEmployeeList(currentUser),
buildEmployees
);
Код API
//(maybe /employees/?search={...}&token=123)
router.get("/employees", (req, res) => {
PersonService.getAll()
.then(handleEmployeeRequest(req.user, req.query.search))
.then(filteredEmployees => res.json(filteredEmployees));
});
И все готово.
Легко, как пирог.
Здесь другая версия с примитивной функцией curry
:
const samples = ["foo", "bar"];
const exclude = function(item,str) {
return item !== str;
}
function curry(func){
return function(var1){
return function(var2){
return func(var1,var2);
};
};
}
console.log(curry(exclude)('foo')('bar')); // true
console.log(samples.filter(curry(exclude)('foo'))); // ["bar"]