Я читал кучу кода react
, и я вижу такие вещи, которые я не понимаю:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Это функция карри
Сначала рассмотрим эту функцию с двумя параметрами...
const add = (x, y) => x + y
add(2, 3) //=> 5
Вот оно снова в карри...
const add = x => y => x + y
Вот тот же код 1 без функций стрелок...
const add = function (x) {
return function (y) {
return x + y
}
}
Фокус на return
Это может помочь визуализировать это по-другому. Мы знаем, что функции стрелок работают так - давайте обратим особое внимание на возвращаемое значение.
const f = someParam => returnValue
Таким образом, наша функция add
возвращает функцию - мы можем использовать скобки для большей ясности. Текст, выделенный жирным шрифтом, является возвращаемым значением нашей функции add
const add = x => (y => x + y)
Другими словами, add
некоторого числа возвращает функцию
add(2) // returns (y => 2 + y)
Вызов карри функций
Таким образом, чтобы использовать нашу функцию карри, мы должны вызвать ее немного по-другому...
add(2)(3) // returns 5
Это связано с тем, что первый (внешний) вызов функции возвращает вторую (внутреннюю) функцию. Только после вызова второй функции мы действительно получаем результат. Это станет более очевидным, если мы разделим звонки на две строки...
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
Применяя наше новое понимание к вашему коду
related: "Какая разница между связыванием, частичным применением и карри?"
Хорошо, теперь, когда мы понимаем, как это работает, давайте посмотрим на ваш код
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
Мы начнем с представления его без использования функций стрелок...
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
Однако, поскольку функции со стрелками связывают this
лексикой, на самом деле это будет выглядеть примерно так…
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
Может быть, теперь мы можем видеть, что это делает более четко. Функция handleChange
создает функцию для указанного field
. Это удобный метод React, потому что вам необходимо настроить своих собственных слушателей на каждом входе, чтобы обновить состояние ваших приложений. Используя функцию handleChange
, мы можем устранить весь дублированный код, который приведет к настройке прослушивателей change
для каждого поля.
Здорово !
Еще больше стрел
При необходимости можно упорядочить более двух функций стрелок:
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
Функции карри способны на удивительные вещи -
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
Частичное применение
Частичное применение является связанной концепцией. Это позволяет нам частично применять функции, аналогичные карри, за исключением того, что функция не должна быть определена в карри -
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
Вот рабочая демонстрация partial
вы можете поиграть в своем браузере -
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector ('input[name=foo]')
.addEventListener ('keypress', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">
1 Здесь я не должен лексически связать this
потому, что первоначальный add
функция не использует контекст, так что это не важно, чтобы сохранить его в этом случае.
$
использовался для демонстрации концепции, но вы можете назвать ее как хотите. По совпадению , но совершенно не связаны, $
использовался в популярных библиотек , как JQuery, где $
является своего рода глобальной точки входа целой библиотеки функций. Я думаю, что это использовалось и в других. Другой, который вы увидите, - _
, популярный в библиотеках, таких как underscore и lodash. Ни один символ не является более значимым, чем другой; Вы назначаете значение для вашей программы. Это просто правильный JavaScript: D
Понимание доступных синтаксисов функций стрелок даст вам представление о том, какое поведение они представляют, когда "прикован", как в приведенных примерах.
Когда функция стрелки написана без скобок блока, с или без нескольких параметров, выражение, которое составляет тело функции, неявно возвращается. В вашем примере это выражение является другой функцией стрелки.
No arrow funcs Implicitly return `e=>{…}` Explicitly return `e=>{…}`
---------------------------------------------------------------------------------
function (field) { | field => e => { | field => {
return function (e) { | | return e => {
e.preventDefault() | e.preventDefault() | e.preventDefault()
} | | }
} | } | }
Другим преимуществом написания анонимных функций с использованием синтаксиса стрелок является то, что они связаны лексически с областью, в которой они определены. Из 'Функции стрелок' в MDN:
выражение функции имеет более короткий синтаксис по сравнению с выражением функции и лексически связывает this. Функции стрелок всегда анонимные.
Это особенно уместно в вашем примере, учитывая, что он берется из с помощью this
. Например:
Unbound Explicitly bound Implicitly bound
------------------------------------------------------------------------------
function (field) { | function (field) { | field => e => {
return function (e) { | return function (e) { |
this.setState(...) | this.setState(...) | this.setState(...)
} | }.bind(this) |
} | }.bind(this) | }
Общий совет, если вы запутались в каком-либо новом синтаксисе JS и как он будет компилироваться, вы можете проверить babel. Например, копирование кода в babel и выбор предварительной настройки es2015 даст такой результат
handleChange = function handleChange(field) {
return function (e) {
e.preventDefault();
// Do something here
};
};
Думайте об этом так, каждый раз, когда вы видите стрелку, вы заменяете ее function
. function parameters
определяются перед стрелкой.
Итак, в вашем примере:
field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}
а потом вместе:
function (field) {
return function (e) {
e.preventDefault();
};
}
// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there only one argument:
singleParam => { statements }
singleParam => expression
this
.
Пример вашего вопроса - это curried function
которая использует arrow function
и имеет implicit return
для первого аргумента.
Стрелки функций лексически связать этот т.е. они не имеют свои собственные this
аргумент, но принять this
значение из области видимости
Эквивалентом приведенного выше кода будет
const handleChange = (field) {
return function(e) {
e.preventDefault();
/// Do something here
}.bind(this);
}.bind(this);
Еще одна вещь, которую стоит отметить в вашем примере, - это определить handleChange
как const или функцию. Возможно, вы используете его как часть метода класса, и он использует class fields syntax
поэтому вместо того, чтобы связывать внешнюю функцию напрямую, вы должны связать ее в конструкторе класса
class Something{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(field) {
return function(e) {
e.preventDefault();
// do something
}
}
}
Еще одна вещь, которую следует отметить в этом примере, - это разница между неявным и явным возвратом.
const abc = (field) => field * 2;
Выше приведен пример неявного возврата т.е. он принимает поле значения в качестве аргумента и возвращает field*2
результата field*2
которое явно указывает возвращаемую функцию
Для явного возврата вы бы явно указали методу вернуть значение
const abc = () => { return field*2; }
Еще одна вещь, которую следует отметить в отношении функций стрелок, заключается в том, что они не имеют своих собственных arguments
но наследуют их также от родительской области.
Например, если вы просто определите функцию стрелки, как
const handleChange = () => {
console.log(arguments) // would give an error on running since arguments in undefined
}
В качестве альтернативы функции стрелки предоставляют остальные параметры, которые вы можете использовать
const handleChange = (...args) => {
console.log(args);
}
Выражение функции стрелки имеет более короткий синтаксис, чем выражение функции, и не имеет собственных аргументов, аргументов, супер или new.target. Эти выражения функций лучше всего подходят для функций не-метода, и они не могут использоваться в качестве конструкторов. Функция стрелки