Что означают множественные функции стрелок в javascript?

275

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

handleChange = field => e => {
  e.preventDefault();
  /// Do something here
}
  • 4
    Просто ради интереса Кайл Симпсон добавил все пути принятия решений для стрелок в эту блок-схему . Источник: его комментарий к сообщению в блоге Mozilla Hacks под названием ES6 In Depth: Arrow functions
  • 0
    Как есть отличные ответы и теперь щедрость. Не могли бы вы уточнить, что вы не понимаете, ответы ниже не охватывают.
Теги:
ecmascript-6
arrow-functions

6 ответов

475

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

Сначала рассмотрим эту функцию с двумя параметрами...

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

Применяя наше новое понимание к вашему коду

related: "Какая разница между связыванием, частичным применением и карри?"

Хорошо, теперь, когда мы понимаем, как это работает, давайте посмотрим на ваш код

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 для каждого поля.

Здорово !


Еще больше стрел

При необходимости можно упорядочить более двух функций стрелок:

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

Функции карри способны на удивительные вещи -

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 ('keypress', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">

1 Здесь я не должен лексически связать this потому, что первоначальный add функция не использует контекст, так что это не важно, чтобы сохранить его в этом случае.

  • 0
    Это замечательно! Как часто кто-то фактически назначает '$'? Или это псевдоним для этого в реакции? Прошу прощения за мое невежество на последнем, просто любопытно, потому что я не вижу символа, получающего назначение слишком часто на других языках.
  • 2
    @Caperneoignis $ использовался для демонстрации концепции, но вы можете назвать ее как хотите. По совпадению , но совершенно не связаны, $ использовался в популярных библиотек , как JQuery, где $ является своего рода глобальной точки входа целой библиотеки функций. Я думаю, что это использовалось и в других. Другой, который вы увидите, - _ , популярный в библиотеках, таких как underscore и lodash. Ни один символ не является более значимым, чем другой; Вы назначаете значение для вашей программы. Это просто правильный JavaScript: D
Показать ещё 1 комментарий
45

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

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

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

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

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

Изображение 2694

30

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню