자바 스크립트에서 여러 개의 화살표 기능은 무엇을 의미합니까?


472

나는 많은 react코드를 읽었 으며 이해하지 못하는 다음과 같은 것을 본다.

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

11
Kyle Simpson 은 재미를 위해서 화살표의 모든 결정 경로를 이 흐름도에 넣었 습니다 . 출처 : ES6 In Depth : Arrow functions
gfullam

큰 답변과 현상금이 있습니다. 아래 답변이 다루지 않는다는 것을 이해하지 못하는 것에 대해 자세히 설명해 주시겠습니까?
Michael Warner

5
새로운 버전의 책이 있으므로 화살표 기능 순서도의 URL이 깨졌습니다. URL은 raw.githubusercontent.com/getify/You-Dont-Know-JS/1st-ed/…에 있습니다
Dhiraj Gupta

답변:


831

그게 카레 기능입니다

먼저이 기능을 두 가지 매개 변수로 검사하십시오.

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

그러나 화살표 기능은 어휘 적으로 결합하기 때문에 this, 그것은 것입니다 실제로 더 같이 ...

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

어쩌면 이제 우리는 이것이 더 명확하게 작동하는 것을 볼 수 있습니다. handleChange함수 지정하기위한 함수를 생성한다 field. 이는 애플리케이션 상태를 업데이트하기 위해 각 입력에 자체 리스너를 설정해야하므로 편리한 React 기술입니다. 이 handleChange함수 를 사용하면 change각 필드에 대한 리스너를 설정하는 모든 중복 코드를 제거 할 수 있습니다 . 멋있는!

1this 원래 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

카레 기능은 놀라운 일을 할 수 있습니다. 아래에서 우리 $는 두 개의 매개 변수를 가진 커리 함수로 정의되었지만 콜 사이트에서는 여러 개의 인수를 제공 할 수있는 것처럼 보입니다. 커링은의 추상화 인수에 대응 -

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


2
이것은 훌륭합니다! 실제로 얼마나 자주 '$'를 할당합니까? 아니면 반응의 별칭입니까? 마지막에 무지를 용서하십시오. 다른 언어로 할당이 너무 자주 부여되는 기호가 보이지 않기 때문에 궁금합니다.
Caperneoignis 19

7
@Caperneoignis $는 개념을 시연하는 데 사용되었지만 원하는 이름을 지정할 수 있습니다. 공교롭게도하지만 전혀 관계가 $ 있다 jQuery를, 같은 인기있는 라이브러리에 사용 된 $일종의 기능의 전체 라이브러리에 글로벌 엔트리 포인트이다. 나는 그것이 다른 사람들에게도 사용되었다고 생각합니다. _밑줄이나 lodash와 같은 라이브러리에서 대중화되는 또 다른 것이 있습니다 . 어떤 심볼도 다른 심볼보다 더 의미가 없습니다. 당신 의 의미를 지정 하여 프로그램. 그것은 단지 유효한 JavaScript입니다 : D
감사합니다

1
거룩한 frijoli, 좋은 대답. 작전이 수락되기를 바랍니다
mtyson

2
@Blake $사용 방법을 살펴보면 더 잘 이해할 수 있습니다 . 구현 자체에 대해 묻는다면 $값을 받고 x새로운 함수를 반환하는 함수 k => ...입니다. 반환 된 함수의 본문을 살펴보면 함수 여야 함 k (x)을 알 k수 있습니다. 결과가 무엇이든 k (x)다시 $ (...)반환 k => ...됩니다. 붙어, 알려주세요.
감사합니다.

2
이 답변은 작동 방식과이 기술에 어떤 패턴이 있는지 설명했습니다. 어떤 시나리오에서 이것이 더 나은 솔루션인지에 대한 구체적인 내용은 없다고 생각합니다. 어떤 상황에서 abc(1,2,3)보다 이상적이지 않습니다 abc(1)(2)(3). 코드의 논리에 대해 추론하기가 어렵고 함수 abc를 읽기가 어렵고 함수 호출을 읽기가 더 어렵습니다. abc가하는 일만 알기 전에, abc가 반환하는 명명되지 않은 함수가 무엇인지, 그리고 두 번은 확실하지 않습니다.
Muhammad Umer

57

화살표 함수사용 가능한 구문을 이해하면 제공 한 예제에서와 같이 '연쇄 화'했을 때 어떤 동작이 도입되는지 이해할 수 있습니다.

여러 매개 변수가 있거나없는 블록 중괄호없이 화살표 함수를 작성하면 함수 본문을 구성하는식이 암시 적으로 반환됩니다. 귀하의 예에서, 그 표현은 또 다른 화살표 기능입니다.

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가 가리키는 아웃로, 당신이 종종 액세스 반작용 구성 요소의 멤버 함수를 사용하여 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)             |  }

53

일반적인 팁, 만약 당신이 새로운 JS 구문과 그것이 어떻게 컴파일되는지 혼동된다면, babel 을 확인할 수 있습니다 . 예를 들어 babel로 코드를 복사하고 es2015 사전 설정을 선택하면 다음과 같은 출력이 나타납니다.

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

바벨


42

이것을 화살표로 볼 때마다로 바꾸십시오 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

6
어휘 바운드를 언급하는 것을 잊지 마십시오 this.
감사합니다.

30

간단하고 간단한 🎈

짧은 방법으로 작성된 다른 함수를 반환하는 함수입니다.

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
)

2

귀하의 질문에있는 예 는 첫 번째 인수를 curried function사용 arrow function하고 implicit returnfor 를 갖는 예 입니다.

Arrow 함수는이 함수를 사 전적으로 바인딩합니다. 즉, 자체 this인수가 없지만 포함 this범위에서 값을 가져옵니다.

위의 코드와 동등한 것은

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

예제에서 주목할 점은 handleChangeconst 또는 함수로 정의 하는 것입니다. 아마 당신은 클래스 메소드의 일부로 사용하고 있으며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);
}

1

그것은 완전히 관련이 없지만 언급 된 질문에 react uses case (그리고 나는이 SO 스레드에 계속 부딪칩니다) : 이중 화살표 기능의 중요한 측면이 여기에 명시 적으로 언급되어 있지 않습니다. '첫 번째'화살표 (함수) 만 이름이 지정되고 (따라서 런타임에 따라 '구별 가능'), 다음 화살표는 익명이며 React 관점에서 모든 렌더링에서 '새로운'객체로 계산됩니다.

따라서 이중 화살표 기능을 사용하면 모든 PureComponent가 항상 다시 렌더링됩니다.

다음과 같이 변경 핸들러가있는 상위 구성 요소가 있습니다.

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

그리고 다음과 같은 렌더링으로 :

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

handleChange는 입력 또는 클릭에 사용됩니다. 그리고 이것은 모두 작동하며 매우 멋지게 보입니다. 그러나 그것은 완전히 관련되지 않은 상태 변경과 같이 부모가 다시 렌더링하게 만드는 모든 변경 사항은 PureComponents 임에도 불구하고 모든 MyTask를 다시 렌더링한다는 것을 의미합니다.

이것은 '가장 바깥 쪽'화살표를 전달하거나 사용자 정의 shouldUpdate 함수를 작성하거나 사용자 정의 shouldUpdate 함수를 작성하거나 명명 된 함수 작성 (및 수동으로 바인딩)과 같은 기본으로 돌아가는 등 여러 가지 방법으로 완화 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.