JavaScript 中的多个箭头函数是什么意思?

IT技术 javascript ecmascript-6 arrow-functions
2021-01-25 02:27:58

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

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

那是一个柯里化函数

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

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

这是杰出的!有人实际分配“$”的频率如何?或者它是反应中的别名?请原谅我对最后一个的无知,只是好奇,因为我没有看到在其他语言中经常收到任务的符号。
2021-03-14 02:27:58
虽然这个答案解释了它是如何工作的以及这种技术有哪些模式。我觉得没有任何具体说明为什么在任何情况下这实际上都是更好的解决方案。在什么情况下,abc(1,2,3)比 不理想abc(1)(2)(3)代码逻辑难以推理,函数 abc 难以阅读,函数调用更难阅读。在您只需要知道 abc 做什么之前,现在您不确定 abc 返回什么未命名函数 do,并且两次。
2021-03-17 02:27:58
@MuhammadUmer 抱歉,函数式风格的优点无法在一篇短文中概括,尤其是谈论一个虚函数,abc,没有任何意义。我要说的一件事是,柯里化允许在程序时间线的各个调用点提供不同的参数。这在所有参数未在同一时间/地点准备好的情况下很有用。学习替代编程风格的好处是多方面的。如果你很好奇函数式语言为什么普遍使用这些技术,你就必须开始学习去亲眼看看!
2021-03-20 02:27:58
@Caperneoignis$用于演示该概念,但您可以随意命名。巧合但完全无关,$ 在流行的库中使用,例如 jQuery,它$是整个函数库的全局入口点。我认为它也被用于其他人。另一个你会看到的是_,在下划线和 lodash 等库中流行。没有一个符号比另一个更有意义;你的程序分配含义它只是有效的 JavaScript :D
2021-03-27 02:27:58
@Blake 您可以$通过查看它的使用方式来更好地了解它。如果您问的是实现本身,$则是一个接收值x并返回一个新函数的函数k => ...查看返回函数的主体,我们看到k (x)所以我们知道它k也必须是一个函数,无论结果k (x)什么都被放回到 中$ (...),我们知道它返回另一个k => ...,然后继续......如果你还在卡住了,让我知道。
2021-04-03 02:27:58

一般提示:如果您对任何新的 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
  }
}

人们为什么这样做

您是否遇到过需要编写可自定义的函数的情况?或者您必须编写一个具有固定参数(参数)的回调函数,但您需要将更多变量传递给函数但避免全局变量?如果您的回答”,那么这就是如何去做的方式。

例如,我们有一个buttononClick 回调。我们需要传递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

所以这是怎么优越const handleClick = (ev, id) => {ev.preventDefault(); //do somth with id},做onClick="(ev) => handleClick(ev, id);"“---这是方式更具有可读性。在您的版本中,甚至发生了一些事情并不明显event
2021-04-05 02:27:58
@Toskan - 是的,你是对的,版本handleClick(ev, id)在某些时候更明显,但它不是可组合的。检查这个片段:gist.github.com/sultan99/13ef56b4089789a8d115869ee2c5ec47,你会发现柯里化函数非常适合函数组合,这是函数式编程的一个非常重要的部分。
2021-04-07 02:27:58

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

当编写箭头函数时不带大括号,带或不带多个参数,构成函数体的表达式将隐式返回。在您的示例中,该表达式是另一个箭头函数。

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 上的“箭头函数”

一个箭头函数表达式相比具有更短的语法函数表达式和词法结合值。箭头函数总是匿名的

这在您的示例中特别相关,因为它取自 应用。作为由@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
不要忘记提及词法绑定this
2021-04-05 02:27:58