читать оригинал
предисловие
В JavaScript каррирование и антикаррирование — это применение функций высшего порядка. Перед этим мы должны знать, что такое функции высшего порядка. Говоря простым языком, функции можно передавать функциям в качестве параметров. Эта функция называется функцией обратного вызова. , а функция с этим параметром является функцией высшего порядка. Функция обратного вызова вызывается в функции высшего порядка и передает соответствующие параметры. Когда функция высшего порядка выполняется, из-за другой внутренней логики функции обратного вызова , выполнение функции высшего порядка Результаты также разные, очень гибкие, также известные как функциональное программирование.
карри
В JavaScript каррирование функций является важной идеей функционального программирования и важным приложением в функциях высшего порядка.Его смысл в том, чтобы шаг за шагом передавать параметры в функцию, каждый раз передавать некоторые параметры и возвращать более конкретную функцию в получить Для остальных параметров несколько уровней таких функций, которые получают частичные параметры, могут быть вложены в середине, пока не будет возвращен окончательный результат.
1. Самый простой сплит карри
// 原函数
function add(a, b, c) {
return a + b + c;
}
// 柯里化函数
function addCurrying(a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
// 调用原函数
add(1, 2, 3); // 6
// 调用柯里化函数
addCurrying(1)(2)(3) // 6
Каррированная функцияaddCurrying
Каждый раз, когда возвращаемое значение является функцией, а следующий параметр используется в качестве формального параметра.После того, как все три параметра переданы, последняя возвращенная функция выполняет операцию суммирования, которая фактически полностью использует характеристики замыкания. быть реализованным.
2, Carrying General Formula
Вышеуказанные функции Carrying не связаны с функциями более высокого порядка, не обладают универсальностью преобразования в любое количество параметров или неизвестной функции, мы следующей функцией Curry Curry Common Transfer, любая функция может быть преобразована в Carrying.
// ES5 的实现
function currying(func, args) {
// 形参个数
var arity = func.length;
// 上一次传入的参数
var args = args || [];
return function () {
// 将参数转化为数组
var _args = [].slice.call(arguments);
// 将上次的参数与当前参数进行组合并修正传参顺序
Array.prototype.unshift.apply(_args, args);
// 如果参数不够,返回闭包函数继续收集参数
if(_args.length < arity) {
return currying.call(null, func, _args);
}
// 参数够了则直接执行被转化的函数
return func.apply(null, _args);
}
}
Вышеупомянутое в основном использует синтаксис ES5 для достижения и использует многоcall
а такжеapply
, давайте реализуем ту же функцию каррирования общей формулы преобразования способом ES6.
// ES6 的实现
function currying(func, args = []) {
let arity = func.length;
return function (..._args) {
_args.unshift(...args);
if(_args.length < arity) {
return currying.call(null, func, _args);
}
return func(..._args);
}
}
функцияcurrying
Это относительно продвинутое преобразование общего назначения, и параметры могут быть разделены по желанию.Предполагая, что преобразуемая функция имеет несколько формальных параметров, мы можем передать любое количество параметров в любой ссылке для разделения.Например, если5
параметры, которые могут быть переданы в первый раз2
, второй можно передать1
, в третий раз можно передать остальные, и есть другие различные схемы передачи и разбиения параметров, т.к.currying
Параметры собираются внутри и корректируются в соответствии с порядком параметров преобразуемой функции.
Одним из больших преимуществ каррирования является то, что оно может помочь нам реализовать функции с разными функциями путем разделения параметров на основе преобразованной функции, как в следующем примере.
// 被转换函数,用于检测传入的字符串是否符合正则表达式
function checkFun(reg, str) {
return reg.test(str);
}
// 转换柯里化
let check = currying(checkFun);
// 产生新的功能函数
let checkPhone = check(/^1[34578]\d{9}$/);
let checkEmail = check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);
Приведенный выше пример преобразуется в каррированную функцию на основе преобразованной функции и используется_check
Переменная получена, и каждый последующий вызов_check
Передача разных регулярных выражений создаст функциональную функцию, которая обнаруживает разные типы строк.
Это использование также применимо, когда преобразованная функция является функцией более высокого порядка, как в следующем примере.
// 被转换函数,按照传入的回调函数对传入的数组进行映射
function mapFun(func, array) {
return array.map(func);
}
// 转换柯里化
let getNewArray = currying(mapFun);
// 产生新的功能函数
let createPercentArr = getNewArray(item => `${item * 100}%`);
let createDoubleArr = getNewArray(item => item * 2);
// 使用新的功能函数
let arr = [1, 2, 3, 4, 5];
let percentArr = createPercentArr(arr); // ['100%', '200%', '300%', '400%', '500%',]
let doubleArr = createDoubleArr(arr); // [2, 4, 6, 8, 10]
3. Каррирование и связывание
bind
Метод - это метод, который часто используется, его роль заключается в том, чтобы помочь нам вызватьbind
объект контекста внутри функцииthis
Замените его первым параметром, который мы передаем, и вызовите другие параметры, следующие за ним.bind
параметры функции.
// bind 方法的模拟
Object.prototype.bind = function (context) {
var self = this;
var args = [].slice.call(arguments, 1);
return function () {
return self.apply(context, args);
}
}
Вышеуказанный код можно увидеть, на самом деле,bind
метод представляет собой каррированную функцию преобразования, которая будет вызыватьbind
Функция метода преобразуется, то есть через замыкание возвращается каррированная функция, а при выполнении каррированной функции заимствуетсяapply
позвонюbind
Контекст выполнения функции преобразуется вcontext
И выполнить, но эта функция преобразования не такая уж сложная, без разделения параметров, но все параметры передаются при вызове функции.
антикарринг
Идея антикаррирования прямо противоположна каррированию.Если процесс каррирования состоит в том, чтобы разбить функцию на функции с более специфическими функциями, то эффект антикаррирования заключается в расширении применимости функции и создании исходная функция.Функции, принадлежащие определенному объекту, могут использоваться любым объектом.
1. Общая формула защиты от каррирования
Параметр универсального антикаррирования – это метод или функция, которую вы хотите вызывать из других объектов. При вызове универсального метода возвращается функция. Первый параметр этой функции – это объект для выполнения метода, а следующие – параметры - это выполнение метода параметры, которые будут переданы.
// ES5 的实现
function uncurring(fn) {
return function () {
// 取出要执行 fn 方法的对象,同时从 arguments 中删除
var obj = [].shift.call(arguments);
return fn.apply(obj, arguments);
}
}
// ES6 的实现
function uncurring(fn) {
return function (...args) {
return fn.call(...args);
}
}
Давайте на примере прочувствуем применение антикаррирования.
// 构造函数 F
function F() {}
// 拼接属性值的方法
F.prototype.concatProps = function () {
let args = Array.from(arguments);
return args.reduce((prev, next) => `${this[prev]}&${this[next]}`);
}
// 使用 concatProps 的对象
let obj = {
name: "Panda",
age: 16
};
// 使用反柯里化进行转化
let concatProps = uncurring(F.prototype.concatProps);
concatProps(obj, "name", "age"); // Panda&16
Существует еще одно применение антикарринга, которое используется вместо прямого использованияcall
а такжеapply
, такие как обнаружение типов данныхObject.prototype.toString
и др. В прошлом мы использовали его для вызова непосредственно после этого метода.call
Измените контекст и параметры передачи.Если в проекте есть несколько мест, которые должны проверять разные типы данных, это очень хлопотно.Традиционное решение — инкапсулировать его в модуль, который определяет типы данных.
// 常规方案
function checkType(val) {
return Object.prototype.toString.call(val);
}
Если вам нужно инкапсулировать много функций таким образом, это будет хлопотно, и количество кода также увеличится.На самом деле, мы также можем использовать другое решение, которое заключается в использовании общей формулы антикаррирования для преобразования эту функцию и использовать возвращаемую функцию как переменную Receive, поэтому нам нужно инкапсулировать только одинuncurring
Дженерик в порядке.
// 利用反柯里化创建检测数据类型的函数
let checkType = uncurring(Object.prototype.toString);
checkType(1); // [object Number]
checkType("hello"); // [object String]
checkType(true); // [object Boolean]
2. Создавайте антикарринговые функции с помощью вызовов функций
В JavaScript мы часто используем объектно-ориентированное программирование, чтобы установить связь между двумя классами или конструкторами для реализации наследования.Если нам нужно наследование только для того, чтобы надеяться, что экземпляр одного конструктора может использовать методы прототипа другого конструктора, это расточительно проводить громоздкое наследование, а простое наследование отношений между родительским и дочерним классами не так элегантно, лучше, чтобы между ними не было никакой связи.
Function.prototype.uncurring = function () {
var self = this;
return function () {
return Function.prototype.call.apply(self, arguments);
}
}
Предыдущий вопрос расширяется функцией вышеuncurring
Метод полностью решен, как в примере ниже.
// 构造函数
function F() {}
F.prototype.sayHi = function () {
return "I'm " + this.name + ", " + this.age + " years old.";
}
// 希望 sayHi 方法被任何对象使用
sayHi = F.prototype.sayHi.uncurring();
sayHi({ name: "Panda", age: 20}); // I'm Panda, 20 years old.
существуетFunction
распространяется на объект-прототипuncurring
, сложность в том, чтобы понятьFunction.prototype.call.apply
, мы знаем, что вcall
в логике исходного кодаthis
ссылается на функцию, которая ее вызывает, вcall
Внутреннее заменяется первым аргументом функцииthis
, а остальные выполняют функцию как формальные параметры.
пока вFunction.prototype.call.apply
серединаapply
Первый параметр замененногоcall
серединаthis
, это используется для заменыthis
называется в примереuncurring
МетодыF.prototype.sayHi
, так что это эквивалентноF.prototype.sayHi.call
,arguments
Параметры внутри будут переданы вcall
в, покаarguments
Первый элемент точно используется для измененияF.prototype.sayHi
серединаthis
Объект.
Суммировать
Видя это, вы должны иметь предварительное представление о каррировании и антикаррировании, но чтобы умело использовать их в разработке, нам нужно более глубоко понять их внутренний смысл.