多个箭头函数在javascript中意味着什么?

时间:2015-09-25 13:06:32

标签: javascript reactjs ecmascript-6 arrow-functions

我一直在阅读一堆react代码,我看到这样的东西,我不明白:

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

8 个答案:

答案 0 :(得分:574)

这是curried 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函数返回函数 - 我们可以使用括号来增加清晰度。 粗体文本是函数add

的返回值
const add = x => (y => x + y)

换句话说,某个数字的add会返回一个函数

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

调用curried函数

因此,为了使用我们的curried函数,我们必须稍微改变它...

add(2)(3)  // returns 5

这是因为第一个(外部)函数调用返回第二个(内部)函数。只有在我们调用第二个函数之后才能实际获得结果。如果我们将呼叫分成两行,这就更明显了......

const add2 = add(2) // returns function(y) { return 2 + y }
add2(3)             // returns 5

将新的理解应用于您的代码

  

相关:”What’s the difference between binding, partial application, and currying?”

好的,既然我们了解了它是如何运作的,那么让我们来看看你的代码

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个侦听器。酷!

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函数能够令人惊讶。下面我们看到$定义为带有两个参数的curried函数,但在调用站点,看起来好像我们可以提供任意数量的参数。 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




部分申请

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

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">
&#13;
&#13;
&#13;

答案 1 :(得分:49)

了解available syntaxes of arrow functions可以让您了解他们在链接时所引入的行为&#39;就像你提供的例子一样。

如果在没有块括号的情况下编写箭头函数(包含或不包含多个参数),则构成函数体的表达式将隐式返回。在您的示例中,该表达式是另一个箭头函数。

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

使用箭头语法编写匿名函数的另一个优点是它们在词汇上与它们的定义范围绑定。来自'Arrow functions' on MDN

  

function expressions相比,箭头函数表达式具有更短的语法,并且词法绑定this值。箭头函数始终为anonymous

考虑到它来自应用程序,这在您的示例中尤为重要。正如@naomik所指出的,在React中,您经常使用this访问component's member functions。例如:

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

答案 2 :(得分:41)

一般提示,如果您对任何新的JS语法及其编译方式感到困惑,可以查看babel。例如,在babel中复制代码并选择es2015预设将产生这样的输出

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

babel

答案 3 :(得分:34)

可以这样想,每次看到箭头时,都会将其替换为function。在箭头前定义为function parameters
所以在你的例子中:

field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}

然后在一起:

function (field) { 
    return function (e) { 
        e.preventDefault(); 
    };
}

From the docs

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

答案 4 :(得分:6)

简洁

这是一个函数,它返回另一个以简短方式编写的函数。

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多少次,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
)

答案 5 :(得分:1)

您问题中的示例是一个curried function的示例,该示例使用arrow function,并且第一个参数的名称为implicit return

Arrow函数以词法绑定此对象,即它们没有自己的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,该结果明确指定要返回的函数

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

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

答案 6 :(得分:1)

它可能并不完全相关,但是由于提到的问题react使用案例(并且我一直碰到这个SO线程):双箭头功能的一个重要方面在这里没有明确提及。 只有“第一个”箭头(函数)被命名(因此在运行时是“可区分的”),随后的任何箭头都是匿名的,从React的角度来看,每个箭头都被视为每个渲染上的“新”对象。

因此,双箭头功能将导致任何PureComponent始终重新渲染。

示例

您的父组件的更改处理程序为:

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
</div>
<img src="https://wallpaper-gallery.net/top/wallpapers-1.jpg" />

并带有如下渲染:

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

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

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

答案 7 :(得分:0)

 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:通用类型

它将更改函数的类型,但结果不正确。