화살표 함수(Arrow Function)

ES6가 도입되면서 새롭게 나타난 화살표 함수(Arrow Function)function 키워드 대신 화살표(=>)를 사용하여 기존의 전통적인 함수표현(function)의 간편한 대안으로 새롭게 등장했다.
화살표 함수의 기본 문법은 아래와 같이 작성한다.

// 기존
function logText(text){
    console.log(text);
}
// 화살표 함수
const logText = (text) => console.log(text)
// 매개변수 지정
   () => { ... } // 매배변수가 없을 때
   x  => { ... } // 매개변수가 한 개
(x,y) => { ... } // 매개변수가 여러 개, 소괄호를 생략할 수 없다.

// 함수 몸체 지정
x => { return x + 10 } // single line block
x => x * 10 // 함수 몸체가 한 줄의 구문이라면 중괄호를 생략할 수 있으며, 암묵적으로 return

() => {return {name: 'siksik'}}; 
() => ({name: 'siksik'}); // 객체를 반환 시 소괄호를 사용해야 한다.

() => {                 // multi line blok.
    const x = 100;
    return x;
}

이와 같이 기존의 전통적인 함수표현과 화살표 함수는 기능적으로는 동일하다. 하지만 왜 화살표 함수가 등장했으며 사용하는 것일까? 단순하게 함수 표현이 간단하기 때문에 사용하는 것일까?

심지어 화살표 함수는 아래와 같은 조건이 있다.

  • 무조건 익명함수로만 사용해야 한다.
  • 메소드나 생성자 함수로 사용할 수 없다.
  • yield를 화살표 함수 내부에서 사용할 수 없다.
  • 일반적으로 스코프를 지정할 때 사용하는 call, apply, bind 메서드를 사용할 수 없다.

결론부터 말하면, 화살표 함수를 사용하는 이유는 this 바인딩 때문이다. 기존의 this 바인딩과 화살표 함수의 this 바인딩에는 무슨 차이가 있는지 알아보자.


기존의 함수의 this 바인딩

화살표 함수가 등장하기 전까지는, 모든 새로운 함수는, 그 함수가 호출되는 방법에 따라서 참조하는 this가 정의되었다.

  • 일반 함수로 호출되는 경우 => 전역객체를 this
  • 메소드로서 호출되는 경우 => 메소드를 호출한 객체를 this
  • call, apply, bind 메서드 호출 => 첫 번째 인자로 넘겨주는 것이 this

이렇게 함수 호출 방식에 따라 참조하는 this 객체가 다르기 때문에 킹받는 일이 많다…

가령 아래와 같은 예시를 들 수 있다.

// 일반 함수로서 호출은 전역객체를 this로 참조함
const person = {
  name: 'siksik',
  address : function() {
      const addr = function(){
          console.log(this.name);
      }
      addr() 
  }
};

person.address(); // undefined 

// 메서드로서 호출은 그 객체를 this로 참조함
const person = {
  name: 'siksik',
  address : function() {
      console.log(this.name); 
  }
};

person.address(); // siksik

첫 번째 함수는

  • person.address() 메서드가 실행됨.
  • addr은 일반 함수로서의 호출이므로 전역객체를 this로 참조함
  • 전역객체에는 name이라는 멤버가 존재하지 않으므로 undefined 출력

두 번째 함수는

  • person.address() 메서드가 실행됨.
  • addr은 메서드로서 호출이므로 그 객체를 this로 참조함
  • 해당 객체에 name이라는 멤버가 존재하므로 siksik 출력

이렇게 참조 객체를 하나하나 신경쓰는게 짜증나는 경우가 많다. 그래서 등장한 화살표 함수!!


화살표 함수의 this 바인딩

기존의 함수는 선얼할 때 this에 바인딩할 객체가 호출 방식에 따라 동적으로 결정되었다면, 화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다.
쉽게 말해, 화살표 함수의 this는 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다.
달리 말하면, 화살표 함수는 자신의 this가 존재하지 않는다!

자바스크립트는 어떤 식별자(변수)를 탐색할 때 현재 환경에서 그 변수가 없으면 바로 상위 환경을 검색한다. 또 없으면 그 상위, 이렇게 상위 환경으로 변수를 올라가면서 찾으며 가장 최상위 환경에 도달할 때 까지도 찾지 못하면 없다고 결론되는 것이다. 화살표 함수는 자신의 this가 존재하지 않으므로 그 상위 this를 동적으로 참조하게 되는 것이다.

// 일반 함수로서 호출은 전역객체를 this로 참조함
const person = {
  name: 'siksik',
  address : function() {
      const addr = () => {
          console.log(this.name);
      }
      addr() 
  }
};

person.address(); // siksik 


화살표 함수, 이럴 땐 사용하지 말자

화살표 함수는 렉시컬 this를 지원하므로 콜백 함수로 사용하기에는 매우~편리하다. 하지만 화살표 함수를 사용하면 오히려 혼란이 오는 경우가 있다.

메소드

화살표 함수를 메소드로 정의하는 것은 피해야 한다. 아래의 코드를 보자

const person = {
  name: 'siksik',
  callName: () => console.log(this.name),
}

person.callName() // undefined 

위 코드를 보면, 메소드로 정의한 화살표 함수의 내부 this는 메소드를 소유한 객체를 참조하지 않고 상위 컨텍스트인 전역 객체를 참조한다. 따라서, 화살표 함수로 메소드를 정의하는 것은 바람직하지 않다. 이럴 땐, 메소드 단축 표기법인 ES6의 축약 메소드 표현을 사용하는 것이 좋다.

const person = {
  name: 'siksik',
  callName(){
      console.log(this.name);
  },
}

person.callName() // siksik

prototype

화살표 함수로 정의된 메소드를 prototype에 할당하는 경우도 동일한 문제가 발생한다. 화살표 함수로 정의된 메소드를 prototype에 할당해보면 알 수 있다

const person = {
    name: 'siksik'
};

Object.prototype.callName = () => console.log(this.name);

person.callName(); // undefined

prototype에 메소드를 할당하는 경우에는 일반 함수를 사용한다.

const person = {
    name: 'siksik'
};

Object.prototype.callName = function() { console.log(this.name) }

person.callName(); // siksik

생성자 함수

화살표 함수는 생성자 함수로 사용할 수 없다. 기본적으로 new 키워드를 붙이는 생성자 함수는 prototype 프로퍼티를 가지게 된다. 이때 prototype 프로퍼티가 참조하는 prototype 객체의 constructor를 사용한다. 하지만, 화살표 함수는 prototype 프로퍼티를 가지고 있지 않기 때문에 사용할 수 없는 것이다.

const Arrow = () => {};
const normal = function(){};

console.log(Arrow.hasOwnProperty('prototype')) // false
console.log(normal.hasOwnProperty('prototype')) // true

const Arrow2 = new Arrow(); // TypeError: Arrow is not a constructor
const normal2 = new normal(); // 생성 완료!

addEventListener 함수의 콜백 함수

addEventListener 함수의 콜백 함수를 화살표 함수로 정의하면 this가 상위 객체를 참조하기 때문에 전역 객체인 window를 참조한다.

var button = document.getElementById('myButton');

button.addEventListener('click', () => {
  console.log(this === window); // true
});

addEventListener 함수의 콜백 함수 내에서 this를 사용하는 경우에는 일반 함수를 사용해야 함수 내부의 this가 해당 이벤트 리스너에 바인딩된 요소(currentTarget)를 가르킨다.

var button = document.getElementById('myButton');

button.addEventListener('click', function() {
  console.log(this === button); // true
});


정리해서

화살표 함수를 사용하는 이유에 대해서 알아봤다. 정리해서, 화살표 함수를 사용하는 이유는 자바스크립트의 this 때문에 사용한다고 결론내리면 된다.
하지만, 그렇다고 무조건 화살표 함수만 사용해야지~는 절대 금지!!! 상황에 따라서 사용하자.