Что означает несколько функций стрелок в javascript?

Я читал кучу кода react, и я вижу такие вещи, которые я не понимаю:

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

Ответ 1

Это функция карри

Сначала рассмотрим эту функцию с двумя параметрами & 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

Ответ 2

Понимание доступных синтаксисов функций стрелок даст вам представление о том, какое поведение они представляют, когда "прикован", как в приведенных примерах.

Когда функция стрелки написана без скобок блока, с или без нескольких параметров, выражение, которое составляет тело функции, неявно возвращается. В вашем примере это выражение является другой функцией стрелки.

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. Функции стрелок всегда анонимные.

Это особенно уместно в вашем примере, учитывая, что он берется из

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

Ответ 3

Общий совет, если вы запутались в каком-либо новом синтаксисе JS и как он будет компилироваться, вы можете проверить babel. Например, копирование кода в babel и выбор предварительной настройки es2015 даст такой результат

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

babel

Ответ 4

Думайте об этом так, каждый раз, когда вы видите стрелку, вы заменяете ее 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

Ответ 5

Кратко и просто 🎈

Это функция, которая возвращает другую функцию, написанную кратко.

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
)

Ответ 6

Пример вашего вопроса - это 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);
}

Ответ 7

Выражение функции стрелки имеет более короткий синтаксис, чем выражение функции, и не имеет собственных аргументов, аргументов, супер или new.target. Эти выражения функций лучше всего подходят для функций не-метода, и они не могут использоваться в качестве конструкторов. Функция стрелки

Ответ 8

 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: универсальный тип

Это изменит тип функции, но результата нет.