JavaScript/함수

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

DevStory 2022. 10. 9.

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

JavaScript ECMAScript 6부터 도입된 화살표 함수(arrow function)는 function 키워드를 사용하여 함수를 정의하는 것보다 적은 타이핑을 요구하고 깔끔한 코드를 작성할 수 있으며, React 클래스 컴포넌트의 생성자에서 이벤트 핸들러 함수를 바인딩하지 않아도 된다는 장점이 존재합니다.

 

하지만, 모든 기능은 장점이 있으면, 단점도 존재합니다. function 키워드 또는 리터럴 방식으로 정의된 함수와 화살표 함수는 동일하게 동작하지 않습니다. 따라서, 이 둘의 차이점을 모르면, 화살표 함수를 잘못 활용할 수 있습니다.

 

이번 포스팅은 JavaScript에서 화살표 함수를 사용하면 안 되는 경우에 대해 소개합니다.


객체의 메서드

객체의 메서드를 화살표 함수로 정의하며, 메서드 본문에 this 키워드를 사용하는 경우 원하는 대로 동작하지 않습니다.

 

다음 예제는 숫자 타입인 a, b 프로퍼티가 존재하고 a와 b의 합을 반환하는 sum() 메서드를 화살표 함수로 정의하였습니다.

const testObj = {
  a: 10,
  b: 20,
  sum: () => {
    console.log(this);
    return (this.a + this.b);
  }
}

console.log(testObj.a);
console.log(testObj.b);
console.log(testObj.sum());

[실행 결과]

위 예제를 실행하면, sum() 메서드 본문에 작성된 this는 sum() 메서드를 호출한 객체인 testObj가 아니라 JavaScript 전역 객체인 window 객체입니다.

 

따라서, sum() 메서드 본문의 this.a는 window.a와 동일하고 this.b는 window.b와 동일합니다. 그리고 window 객체에 a와 b라는 프로퍼티가 존재하지 않으므로 this.a + this.b의 결과로 NaN이 반환됩니다.

 

sum() 메서드의 this가 전역 객체인 window로 설정된 이유는 화살표 함수의 this는 화살표 함수를 호출한 녀석(객체, 함수)의 상위 this로 설정되기 때문입니다.

 

위 문제를 해결하려면 화살표 함수가 아닌 JavaScript 고전적인 방식으로 메서드를 정의합니다.

const testObj = {
  a: 10,
  b: 20,
  sum: function() {
    return this.a + this.b;
  }
}

console.log(testObj.a);
console.log(testObj.b);
console.log(testObj.sum());

[실행 결과]


프로토타입

프로토타입의 메서드를 정의하는 경우에도 동일한 규칙이 적용됩니다.

 

다음 예제는 a와 b의 합을 반환하는 메서드를 객체의 프로토타입으로 정의합니다.

function testObj() {
  this.a = 10;
  this.b = 20;
}

testObj.prototype.sum = () => {
  return this.a + this.b;
}

var obj = new testObj();

console.log(obj.a);     // 10;
console.log(obj.b);     // 20;
console.log(obj.sum()); // NaN

sum() 메서드의 this가 전역 객체인 window로 설정되었기에 this.a + this.b는 NaN을 반환합니다.

 

따라서, 화살표 함수가 아닌 JavaScript 고전적인 방식으로 프로토타입의 메서드를 정의합니다.

function testObj() {
  this.a = 10;
  this.b = 20;
}

testObj.prototype.sum = function() {
  return this.a + this.b;
}

var obj = new testObj();

console.log(obj.a);     // 10
console.log(obj.b);     // 20
console.log(obj.sum()); // 30

이벤트 핸들러 함수

React, Svelte와 같은 프레임워크가 등장하면서 이벤트 핸들러 함수를 등록하는 방법이 쉬워졌습니다. 기존에는 getElementById() 메서드를 사용하여 id를 가져온 뒤 addEventListener() 메서드로 이벤트 핸들러 함수를 등록했습니다.

 

다음 예제는 id가 'name'인 태그를 가져온 뒤 클릭 이벤트가 동작하면, 해당 태그의 innerHTML을 변경합니다.

const button = document.getElementById('name');

button.addEventListener('click', () => {
  this.innerHTML = '클릭!';
});

클릭 이벤트의 핸들러 함수를 화살표 함수로 정의하였으며, 해당 태그의 innerHTML을 변경하기 위해 this를 사용했습니다. 하지만, 이벤트 핸들러 함수는 정상적으로 동작하지 않습니다. 화살표 함수 본문에 정의된 this.innerHTML은 window.innerHTML과 동일하기 때문입니다.

 

따라서, 이벤트 핸들러 함수가 정상적으로 동작하도록 함수 표현식을 적용합니다.

const button = document.getElementById('name');

button.addEventListener('click', function(){
  this.innerHTML = '클릭!';
});

리팩토링

화살표 함수의 중괄호와 return문을 제거하여 코드를 간결하게 만들 수 있지만, 지나친 리팩토링은 오히려 읽기 어려운 코드를 구현합니다. 예를 들어, 첫 번째 매개변수에 따라 두 번째 매개변수에 10을 더하거나 10을 뺄셈하는 코드를 다음 코드처럼 구현할 수 있습니다.

const cal = (gubun, num) => gubun === '+' ? num + 10 : gubun === '-' ? num - 10 : num;

console.log(cal('+', 100));  // 110
console.log(cal('-', 100));  // 90
console.log(cal('HI', 100)); // 100

위 코드는 문제없이 동작하지만, 제 3자 입장에서 코드가 어떻게 동작하는지 한눈에 파악하기 어렵습니다.

 

때로는 지나친 리팩토링보다 한눈에 파악할 수 있는 코드를 작성하는 것이 좋습니다.

const cal = (gubun, num) => {
  if(gubun === '+') {
    return num + 10;
  } else if(gubun === '-') {
    return num - 10;
  } else {
    return num;
  }
}

console.log(cal('+', 100));  // 110
console.log(cal('-', 100));  // 90
console.log(cal('HI', 100)); // 100
반응형

댓글