Я читал кучу кода react
, и я вижу такие вещи, которые я не понимаю:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Я читал кучу кода react
, и я вижу такие вещи, которые я не понимаю:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Это функция карри
Сначала рассмотрим эту функцию с двумя параметрами & hellip;
const add = (x, y) => x + y
add(2, 3) //=> 5
Здесь снова в карри -
const add = x => y => x + y
Вот тот же код 1 без функций стрелок & hellip;
const add = function (x) {
return function (y) {
return x + y
}
}
Сосредоточьтесь на return
Это может помочь визуализировать это по-другому. Мы знаем, что функции стрелок работают следующим образом - давайте обратим особое внимание на возвращаемое значение return value.
const f = someParam => returnValue
Итак, наша функция add
возвращает функцию - мы можем использовать скобки для большей ясности. Текст , выделенный полужирным шрифтом, является возвращаемым значением нашей функции add
const add = x => (y => x + y)
Другими словами, add
некоторого числа возвращает функцию
add(2) // returns (y => 2 + y)
Вызов карри-функций
Таким образом, чтобы использовать нашу функцию карри, мы должны назвать ее немного по-другому & hellip;
add(2)(3) // returns 5
Это потому, что первый (внешний) вызов функции возвращает вторую (внутреннюю) функцию. Только после вызова второй функции мы действительно получаем результат. Это станет более очевидным, если мы разделим вызовы на две строки & hellip;
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
Применяя наше новое понимание к вашему коду
related: 'Whats the difference between binding, partial application, и currying?'
Хорошо, теперь, когда мы понимаем, как это работает, давайте посмотрим на ваш код
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
Начнем с представления его без использования функций со стрелками & hellip;
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
Тем не менее, поскольку функции стрелок лексически связывают this
, на самом деле это будет выглядеть примерно так & hellip;
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
Может быть, теперь мы можем видеть, что это делает более четко. Функция handleChange
создает функцию для указанного field
. Это удобный метод React, потому что вам необходимо настроить своих собственных слушателей на каждом входе, чтобы обновить состояние ваших приложений. Используя функцию handleChange
, мы можем исключить весь дублированный код, который приведет к настройке прослушивателей change
для каждого поля. Круто!
1 Здесь мне не нужно было лексически связывать this
, потому что исходная функция add
не использует никакого контекста, поэтому не важно сохранять ее в этом случае.
Еще больше стрелок
При необходимости можно упорядочить более двух функций стрелок -
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
Функции Curried способны удивлять. Ниже мы видим, что $
определен как карри-функция с двумя параметрами, но на сайте вызова кажется, что мы можем предоставить любое количество аргументов. Карри - это абстракция арности -
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
Понимание доступных синтаксисов функций стрелок даст вам представление о том, какое поведение они представляют, когда "прикован", как в приведенных примерах.
Когда функция стрелки написана без скобок блока, с или без нескольких параметров, выражение, которое составляет тело функции, неявно возвращается. В вашем примере это выражение является другой функцией стрелки.
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
Кратко и просто 🎈
Это функция, которая возвращает другую функцию, написанную кратко.
const handleChange = field => e => {
e.preventDefault()
// Do something here
}
// is equal to
function handleChange(field) {
return function(e) {
e.preventDefault()
// Do something here
}
}
Почему люди это делают?
Сталкивались ли вы когда вам нужно написать функцию, которую можно настроить? Или вам нужно написать функцию обратного вызова, которая имеет фиксированные параметры (аргументы), но вам нужно передать больше переменных в функцию, но избегая глобальных переменных? Если ваш ответ "да", то это способ, как это сделать.
Например, у нас есть button
с обратным вызовом onClick. И нам нужно передать id
в функцию, но onClick
принимает только одно event
параметра, мы не можем передать дополнительные параметры следующим образом:
const handleClick = (event, id) {
event.preventDefault()
// Dispatch some delete action by passing record id
}
Она не будет работать!
Поэтому мы создаем функцию, которая будет возвращать другую функцию с собственным диапазоном переменных без каких-либо глобальных переменных, потому что глобальные переменные являются злом 😈.
Ниже будет handleClick(props.id)}
функция handleClick(props.id)}
которая возвратит функцию и у нее будет id
в своей области видимости! Независимо от того, сколько раз он будет нажат, идентификаторы не будут влиять или изменять друг друга, они полностью изолированы.
const handleClick = id => event {
event.preventDefault()
// Dispatch some delete action by passing record id
}
const Confirm = props => (
<div>
<h1>Are you sure to delete?</h1>
<button onClick={handleClick(props.id)}>
Delete
</button>
</div
)
Пример вашего вопроса - это 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. Эти выражения функций лучше всего подходят для функций не-метода, и они не могут использоваться в качестве конструкторов. Функция стрелки
var handleChange = field => e => {
e.preventDefault();
/// Do something here
}
В Ecma5 переведите:
"use strict";
var handleChange = function handleChange(field) {
return function (e) {
e.preventDefault(); /// Do something here
};
};
var f = function(x,y) { return x+y }
var g = function(x) { return function(y) { return x+y }}
f: (T x T) -> T
g: T -> T -> T
T: универсальный тип
Это изменит тип функции, но результата нет.