繁体   English   中英

JavaScript 中的多个箭头功能是什么意思?

[英]What do multiple arrow functions mean in JavaScript?

我一直在阅读一堆React代码,但我看到了一些我不明白的东西:

 handleChange = field => e => { e.preventDefault(); /// Do something here }

那是一个咖喱 function

首先,用两个参数检查这个 function……

 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 function 返回一个function - 我们可以使用括号来增加清晰度。 加粗的文本是我们的add的返回值

const add = x => (y => x + y)

换句话说, add一些数字会返回 function

 add(2) // returns (y => 2 + y)

调用柯里化函数

因此,为了使用我们的咖喱 function,我们必须稍微不同地称呼它……

 add(2)(3) // returns 5

这是因为第一个(外部)function 调用返回第二个(内部)function。 只有调用第二个 function 后,我们才能真正得到结果。 如果我们将调用分开在两条线上,这一点会更加明显……

 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... }; };

然而,因为箭头函数在词法上绑定this ,它实际上看起来更像这样……

 handleChange = function(field) { return function(e) { e.preventDefault() // Do something here // return... }.bind(this) }.bind(this)

也许现在我们可以更清楚地看到它在做什么。 handleChange function 正在为指定的field创建 function 。 这是一种方便的 React 技术,因为您需要在每个输入上设置自己的侦听器才能更新您的应用程序 state。 通过使用handleChange function,我们可以消除所有会导致为每个字段设置change侦听器的重复代码。 凉爽的

1这里我不必对this进行词法绑定,因为原来的add function 没有使用任何上下文,所以在这种情况下保留它并不重要。


更多的箭头

如有必要,可以对两个以上的箭头函数进行排序 -

 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

柯里化函数能够带来令人惊讶的事情。 下面我们看到$定义为带有两个参数的柯里化 function,但在调用站点,似乎我们可以提供任意数量的 arguments。 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

部分应用

部分应用是一个相关的概念。 它允许我们部分应用函数,类似于柯里化,除了 function 不必以柯里化形式定义 -

 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">

简要地

它是一个 function 返回另一个 function 以简短的方式编写。

 const handleChange = field => e => { e.preventDefault() // Do something here } // is equal to function handleChange(field) { return function(e) { e.preventDefault() // Do something here } }

为什么?

您是否曾经遇到过需要创建可定制的 function 的情况? 或者你有一个带有固定参数的回调 function,但是你需要在避免全局变量的同时发送额外的变量? 如果您回答,那么这就是如何做到这一点的方法。

例如,我们有一个带有onClick回调的按钮。 我们想将id传递给 function,但是, onClick只接受一个参数event ,所以我们不能这样做:

 const handleClick = (event, id) { event.preventDefault() // Dispatch some delete action by passing record id }

这是行不通的

作为一种解决方案,我们编写了一个 function ,它返回另一个 function ,其变量 scope 中包含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 )

Function组成

多个箭头函数也称为“curried 函数”,它们用于 function 组合。

 // It is just an example, unfortunately, redux does not export dispatch function import {dispatch, compose} from 'redux' const pickSelectedUser = props => { const {selectedName, users} = props const foundUser = users.find(user => user.name === selectedName) return foundUser.id } const deleteUser = userId => event => { event.preventDefault() dispatch({ type: `DELETE_USER`, userId, }) } // The compose function creates a new function that accepts a parameter. // The parameter will be passed throw the functions from down to top. // Each function will change the value and pass it to the next function // By changing value it was not meant a mutation const handleClick = compose( deleteUser, pickSelectedUser, ) const Confirm = props => ( <div> <h1>Are you sure to delete?</h1> <button onClick={handleClick(props)}> Delete </button> </div )

一般提示:如果您对任何新的 JavaScript 语法及其编译方式感到困惑,您可以查看Babel 例如,在 Babel 中复制您的代码并选择 ES 2015 预设将得到像这样的 output

 handleChange = function handleChange(field) { return function (e) { e.preventDefault(); // Do something here }; };

通天塔

了解箭头函数的可用语法将使您了解它们在“链接”时引入的行为,就像您提供的示例中一样。

当箭头 function 被写入时,无论有无多个参数,都会隐式返回构成函数体的表达式。 在您的示例中,该表达式是另一个箭头 function。

 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() } | | } } | } | }

使用箭头语法编写匿名函数的另一个优点是它们在词法上绑定到定义它们的 scope。 来自MDN 上的“箭头函数”

function表达式相比,箭头 function 表达式的语法更短,并且在词法上绑定了this值。 箭头函数总是匿名的。

考虑到它取自应用程序,这在您的示例中尤其相关。 正如@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

它可能不完全相关,但由于提到的问题反应用例(我一直碰到这个 SO 线程):双箭头 function 的一个重要方面在这里没有明确提到。 只有“第一个”箭头(函数)被命名(因此在运行时“可区分”),任何后面的箭头都是匿名的,从 React 的角度来看,每次渲染都算作一个“新的”object。

因此双箭头 function 将导致任何 PureComponent 一直重新渲染。

例子

您有一个带有更改处理程序的父组件:

 handleChange = task => event => {... operations which uses both task and event... };

并使用如下渲染:

{ tasks.map(task => <MyTask handleChange={this.handleChange(task)}/> }

然后将 handleChange 用于输入或单击。 这一切都有效,看起来非常好。 但这意味着任何会导致父级重新渲染的更改(例如完全不相关的 state 更改)也会重新渲染您的所有 MyTask,即使它们是 PureComponents。

这可以通过多种方式得到缓解,例如传递“最外”箭头和 object,您将使用它或编写自定义 shouldUpdate function 或返回基础知识,例如编写命名函数(并手动绑定 this...)

您问题中的示例是使用arrow functionimplicit return第一个参数的curried function

箭头 function 词法绑定 this 即他们没有自己的this参数,但从封闭的 scope 中获取this

与上述代码等效的是

const handleChange = (field) { return function(e) { e.preventDefault(); /// Do something here }.bind(this); }.bind(this);

关于您的示例要注意的另一件事是将handleChange定义为 const 或 function。 可能您将它用作 class 方法的一部分,它使用class fields syntax

所以不要直接绑定外部 function ,而是在 class 构造函数中绑定它

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 ,明确指定要返回的 function

对于显式返回,您将显式告诉方法返回值

const abc = () => { return field*2; }

关于箭头函数需要注意的另一件事是,它们没有自己的arguments ,但也从父级 scope 继承。

例如,如果您只定义一个箭头 function 像

const handleChange = () => { console.log(arguments) // would give an error on running since arguments in undefined }

作为替代箭头函数,您可以使用 rest 参数

const handleChange = (...args) => { console.log(args); }
 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:泛型

它会改变函数的类型,但结果没有。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM