我一直在阅读一堆React代码,但我看到了一些我不明白的东西:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
我一直在阅读一堆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
将我们的新理解应用于您的代码
好的,现在我们了解了它是如何工作的,让我们看看你的代码
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
我们将首先在不使用箭头函数的情况下表示它......
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
然而,因为箭头函数在词法上是 bind this
,它实际上看起来更像这样......
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
柯里化函数能够产生令人惊讶的东西。下面我们看到$
定义为带有两个参数的柯里化函数,但在调用点,看起来我们可以提供任意数量的参数。Currying 是arity的抽象——
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 ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">
一般提示:如果您对任何新的 JavaScript 语法及其编译方式感到困惑,您可以查看Babel。例如,在 Babel 中复制您的代码并选择 ES 2015 预设将提供如下输出
handleChange = function handleChange(field) {
return function (e) {
e.preventDefault();
// Do something here
};
};
简明扼要🎈
它是一个函数,它返回另一个以简短方式编写的函数。
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)}
将被调用并返回一个函数,它将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
)
其他福利
返回另一个函数的函数,也称为“柯里化函数”,它们用于函数组合。
您可以在这里找到示例:https : //gist.github.com/sultan99/13ef56b4089789a8d115869ee2c5ec47
了解箭头函数的可用语法将使您了解它们在像您提供的示例中那样“链接”时引入的行为。
当编写箭头函数时不带大括号,带或不带多个参数,构成函数体的表达式将隐式返回。在您的示例中,该表达式是另一个箭头函数。
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 上的“箭头函数”:
这在您的示例中特别相关,因为它取自 react应用。作为由@naomik指出的那样,在你做出react经常访问组件的成员函数使用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) | }
可以这样想,每次看到箭头时,都将其替换为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's only one argument:
singleParam => { statements }
singleParam => expression