본문 바로가기
Programming/자바스크립트

[자바스크립트] Arrow Function

by 코딩하는 랄로 2023. 10. 26.
728x90

화살표 함수 (Arrow Function)

화살표 함수는 ES6에서 도입된 문법으로 function 키워드 대신 화살표(=> , arrow)를 사용하여 함수를 선언할 수 있다. 화살표 함수가 분명 함수를 선언하는데에 편리한 방법이기는 하지만 사용할 수 없는 상황이 존재하기 때문에 정확하게 아는 것이 중요하다.

 

먼저, 화살표 함수의 기본적인 문법은 아래와 같다.

// 화살표 함수 기본 문법

// 매개변수 지정 방법
// 1. 매개변수가 없을 때
() => { } // <=  () 필수

// 2. 매개변수가 한개일 때
n => { } // () 생략 가능

// 3. 매개변수가 여러개일 때
(n1, n2) => { } // () 필수


// 실행문 작성 방법
// single line block : 실행문이 한 줄일때
x => console.log(x) // {} 생략 가능

// return문으로만 실행문이 구성되어 있을 때
x => { return x * x; }
x => x * x // => return 키워드, 중괄호 생략 가능
// => 객체를 반환할 때에, return 키워드를 생략하고 싶다면 () 사용 필수!!
x => ( { a : x } ) 

// multi line block : 실행문이 여러 줄일 때
x => {
    console.log(x);
    return x * x;
}  // {} 필수

 

 

 

화살표 함수 호출

화살표 함수는 익명 함수로만 사용할 수 있기 때문에 화살표 함수를 호출하기 위해서는 함수표현식을 사용해야 한다.

 

// 함수 표현식 사용!!
const printX = x => console.log(x);
printX(4); //4

 

또는 콜백함수로도 사용이 가능하다. 일반적인 함수 표현식보다 표현이 훨씬 간결해진다.

// 콜백 함수
// 일반 함수 표현식
const arr = [1, 2, 3]
arr.map(function (el) {
    console.log(el);
}; 
// 1 2 3 


// 화살표 함수
arr.map(el => console.log(el)); //1 2 3

 

 

 

자바스크립트에서 this

일반함수와 화살표 함수의 가장 큰 차이점은 this이다. 일반적으로 프로그래밍 언어에서 this는 this를 호출한 해당 객체를 일컫는다. 하지만 자바스크립트에서는 this를 어디에서 호출했냐에 따라 해당 객체가 될 수도 또는 전역 객체인 window가 될 수도 있는 등 복잡하게 동작한다.

 

this에 어떤 객체인지 결정되는 것을 바인딩이라고 표현하는데, 자바스크립트에서 상황에 따라 this가 어떤 객체와 바인딩이 되는지를 명확하게 알아야 화살표 함수를 보다 잘 쓸 수 있게 된다.

 

 

 

일반 함수의 this

자바스크립트에서 일반 함수 호출 방식에 의해 this에 바인딩할 객체가 동적으로 결정된다. 쉽게 말하면 일반 함수의 this는 호출 위치에 따라 바인딩되는 객체가 변한다. 이를 이해하기 위해서는 아래의 예제를 살펴보자.

// 일반함수의 동적 바인딩
function Prefixer(prefix) { //생성자 함수
  this.prefix = prefix;
}


// 자바스크립트에서 Object는 prototype 프로퍼티를 상속받는다
Prefixer.prototype.prefixArray = function (arr) {
  console.log('prefix : ' + this.prefix) // (A)
  return arr.map(function (x) {
    return this.prefix + ' ' + x; // (B)
  });
};

const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim'])); // (C)

 

먼저 위의 예제에서 (C) 지점의 실행 결과는 다음과 같다.

// (C) 실행 결과
prefix : Hi
[ 'undefined Lee', 'undefined Kim' ] //window 객체의 prefix는 없으므로

 

왜 이러한 결과가 나올까? 위의 예제에서 (A), (B)의 각 지점에서의 this에 바인딩된 객체를 살펴보면 다음과 같다.

  • (A) : 해당 함수를 호출한 객체 => pre 
  • (B) : 전역 객체인 window

자바스크립트는 생성자 함수와 객체의 메소드를 제외한 모든 함수(내부함수, 콜백함수 포함) 내부의 this는 전역 객체를 가리킨다. 또한 일반함수의 호출은 this를 동적으로 바인딩한다. 즉, 일반함수에서의 this 는 상황에 따라 어떤 객체를 바인딩할지 달라질 수 있다는 것이다.

 

그렇기 때문에 Prefixer.prototype 객체의 prefixArray 메소드를 호출하였을 때는 호출 위치인 해당 객체인 pre 가 바인딩 되었지만 map 메소드를 통해 콜백함수를 호출한 시점에서는 window 객체가 this에 바인딩되었기 때문에 위와 같은 결과가 나오는 것이다.

 

그렇다면 위의 예제에서 콜백 함수 내부의 this가 메소드를 호출한 객체를 가리키게 하려면 어떻게 해야할까? 자바스크립트에는 이를 위한 방법으로 아래의 3가지가 있다.

// Solution1
// 메소드 생성시, this가 호출한 객체를 바인딩하고 있음
// 새로운 변수에 해당 객체를 저장
// 콜백 함수에서 호출한 객체를 저장한 새로운 변수를 사용하여 접근
function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  let that = this;  // this: Prefixer 생성자 함수의 인스턴스
  return arr.map(function (x) {
    return that.prefix + ' ' + x;
  });
};

var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim'])); //['Hi Lee', 'Hi Kim']


// Solution2
// map의 두번째 파라미터 => 콜백함수의 this에 바인딩할 객체
// defualt => window 객체
Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(function (x) {
    return that.prefix + ' ' + x;
  }, this); // this : pre
};

var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim'])); //['Hi Lee', 'Hi Kim']


// Solution3
// ES5에서 추가된 문법
// Function.prototype.bind()로 객체를 바인딩
Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(function (x) {
    return that.prefix + ' ' + x;
  }).bind(this); // this : pre
};

var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim'])); //['Hi Lee', 'Hi Kim']

 

 

 

화살표 함수의 this

화살표 함수에서의 this는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 즉, 바인딩할 객체가 정해진 뒤, 더 이상 해당 this에 바인딩할 객체가 변하지 않음을 의미한다.

 

그렇다면 화살표 함수를 선언할 때 어떤 객체가 this에 정적으로 바인딩 될까? 화살표 함수의 this는 언제나 상위 스코프의 this를 정적으로 바인딩한다. 이를 Lexical this라고 한다.

// 화살표 함수에서의 this
// 무조건 상위 스코프의 this를 가리킴
function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  // this는 상위 스코프인 prefixArray 메소드 내의 this를 가리킨다.
  return arr.map(x => `${this.prefix}  ${x}`);
};

const pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));

 

map 안의 콜백 함수인 화살표 함수의 상위 스코프는 prefixArray 메소드이므로 해당 메소드의 this인 pre를 화살표 함수의 this 가 바인딩하는 것이다.

 

화살표 함수는 정적으로 this를 바인딩하기 때문에 this를 변경할 수 있는 메소드(call, apply, bind)를 사용하여도 변경되지 않는다.

 

 

 

화살표 함수를 사용하면 안되는 경우

화살표 함수는 this에 객체가 정적으로 바인딩된다고 하였는데, 이러한 이유로 화살표 함수를 사용하면 안 되는 경우가 생긴다. 바로 화살표 함수의 정적 바인딩이 자바스크립트가 특정 경우에 정의한 this의 바인딩과 충돌을 일으키는 경우이다. 어떠한 경우가 있는지 아래에서 살펴보자.

 

 

메소드

자바스크립트에서는 객체의 메소드로 정의되는 함수에서 this는 호출한 객체를 바인딩한다. 이 때, 메소드로 화살표 함수를 사용하게 되면, this가 window 객체를 바인딩하게 된다.(객체의 메서드의 상위 스코프는 window 객체이기 때문에!!)

 

그렇기 때문에, 메소드를 화살표 함수로 정의하는 것은 피해야 하며 축약하고 싶다면 ES6에서 도입된 축약 메소드 표현 사용을 권장한다.

// 메소드
// 메소드를 호출한 객체를 바인딩해야 함
// Bad
const person = {
  name: 'Lee',
  sayHi: () => console.log(`Hi ${this.name}`) //this => window
};

person.sayHi(); // Hi undefined


//Good
const person = {
  name: 'Lee',
  sayHi() {  // === sayHi: function() {}
    console.log(`Hi ${this.name}`);  //this = person
  }
};

person.sayHi(); // Hi Lee

 

 

prototype

프로토타입 또한 호출한 객체를 바인딩해야 하는데 화살표 함수로 정의하게 되면 window 객체를 바인딩하게 되기 때문에 사용을 피해야 한다.

// prototype
// 호출한 객체를 바인딩해야 함
// Bad
const person = {
  name: 'Lee',
};

Object.prototype.sayHi = () => console.log(`Hi ${this.name}`); // this = window

person.sayHi(); // Hi undefined


//Good
const person = {
  name: 'Lee',
};

Object.prototype.sayHi = function() {
  console.log(`Hi ${this.name}`);  //this = person
};

person.sayHi(); // Hi Lee

 

 

생성자 함수

화살표 함수는 생성자 함수로 사용할 수 없다. 왜냐하면 생성자 함수는 prototype 프로퍼티를 가지며 prototype 프로퍼티가 가리키는 프로토타입 객체의 constructor를 사용지만 화살표 함수는 prototype 프로퍼티를 가지고 있지 않기 때문이다.

// 생성자 함수
const Foo = () => {};

// 화살표 함수는 prototype 프로퍼티가 없다
console.log(Foo.hasOwnProperty('prototype')); // false

const foo = new Foo(); // TypeError: Foo is not a constructor

 

 

addEventListener 함수의 콜백함수

addEventListener 함수의 경우에도 콜백 함수를 화살표 함수로 정의하면 this가 상위 컨택스트인 전역 객체 window를 가리키기 때문에 문제가 발생한다. (이벤트 리스너를 통해 등록한 콜백 함수의 상위 스코프는 윈도우 객체이기 때문!!)

 

addEventListener 함수에서 this를 event가 발생한 객체를 바인딩하기 위해서는 일반함수를 사용하여 this를 호출하여야 한다. 일반 함수를 호출할 때의 this는 호출한 객체가 bindng 되기 때문이다.

// addEventListener
// 콜백 함수를 호출한 객체를 this에 바인딩 해야 함

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

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


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

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

 

728x90