들어가며
자바스크립트는 함수 선언식, 함수 표현식, 화살표 함수 등 다양한 함수 선언 방식을 지원한다. 개발자에게 선택권이 많다는건 큰 장점이 될 수 있지만 JS에 아직 익숙하지 않은 개발자라면 언제, 어디서, 어떤 함수 선언 방식을 사용할지 고민이 될 것이다. 특히 ECMA 2015(ES6)에서 등장한 화살표 함수는 일반 함수들과 다른 특징을 가지고 있기 때문에 많은 주니어 개발자들에게 혼란을 주기도 한다. 이에 화살표 함수와 일반함수의 차이점을 알아보고 언제 화살표 함수를 사용할지와 화살표 함수를 사용하면서 주의해야할 점을 알아보고자 한다.
일반함수와 화살표 함수의 차이점
1. this 참조 방식
일반함수와 화살표 함수의 가장 큰 차이점이다. 일반함수는 함수 호출시에 동적으로 this를 바인딩하지만 화살표 함수는 this 바인딩하지 않고 외부 컨텍스트의 this를 그냥 참조한다.
일반함수의 this는 다음 규칙에 따라 바인딩된다.
- 함수 실행 시 전역객체(window) 할당
- 메소드시 메소드를 소유하고 있는 해당 객체 할당
- 객체 생성 시 생성된 객체 할당
반면 화살표 함수는 이런 this 바인딩 과정 없이 외부 컨텍스트의 this를 참조한다. 예시를 보자
const obj = {
name: 'hoony',
func1: function () {
console.log(this);
},
func2: () => {
console.log(this);
},
func3: function() {
getName = () => {
console.log(this);
}
getName();
},
func4: function() {
const obj2 = {
subFunc1: () => console.log(this)
};
subFunc = function () {
console.log(this);
}
obj2.subFunc1();
subFunc();
}
};
obj.func1(); // obj 객체
obj.func2(); // window 객체
obj.func3(); // obj 객체
obj.func4(); // 결과는?
- 함수 선언식으로 선언된 func1()은 this 바인딩 규칙에 따라 obj 객체를 참조한다.
- 반면 화살표 함수로 선언된 func2()는 바인딩 과정없이 외부 컨텍스트의 this를 참조하는데 외부 컨텍스트의 this가 없기 때문에 전역 객체인 window를 할당한다.
- func3() 내부에 화살표함수로 선언된 getName의 this는 외부 컨텍스트인 func3()의 this를 참조하는데 func3()는 func1()와 같이 obj 객체를 참조하기 때문에 getName의 this도 obj를 참조한다.
- 3가지 예시를 통해 func4()의 결과를 예상할 수 있는데, func4 내부에서 호출된 obj2.subFunc1()는 상위 컨텍스트의 this를 참조하기 때문에 obj 객체를 참조하고 subFunc()는 바인딩 규칙에 의해 전역 객체인 window를 참조한다.
2. 프로토타입 속성의 존재 여부
일반함수는 프로토타입 속성이 존재하나 화살표함수는 프로토타입 속성을 가지고 있지 않다.
const Hoony = () => {};
console.log(Hoony.hasOwnProperty('prototype')); // false
const foo = new Hoony(); // TypeError: Foo is not a constructor
미리 말하면 프로토타입 속성이 없기 때문에 생성자로 사용할 수 없다.
3. arguments 사용가능 여부
화살표 함수는 arguments 속성을 생성하지 않는다. 따라서 arguments를 사용하면 참조오류가 발생하거나 상위 스코프의 arugments를 참조한다.
언제 화살표 함수를 사용하는가?
1. 간결하고 짧은 함수 작성
function counter(x) {
return x++;
}
const Counter = x => x++;
function을 ⇒ 로 축약할 수 있고 매개변수의 괄호, return 명령어, 함수 괄호 {}와 같이 많은 내용을 생략할 수 있다. 간단한 함수를 작성할 때 간결하고 짧고 빠르게 함수를 작성할 수 있다.
2. 콜백함수
class Shape {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
printArea() {
console.log(this.getArea());
}
printAsync1() {
setTimeout(this.printArea(), 2000);
}
printAsync2() {
setTimeout(() => {this.printArea()}, 2000);
}
}
let shape = new Shape(20, 10);
shape.printAsync1(); // TypeError
shape.printAsync2(); // 200
setTimeout은 콜백함수 this를 전역객체로 바인딩하기 때문에 printAsync1()가 TypeError가 발생한다. 화살표함수는 bind 과정을 생략하기 때문에 printAsync2()의 setTimeout 콜백함수의 this는 Shape 객체를 가르키게 되고 의도되로 작동하는 것을 알 수 있다.
이처럼 콜백함수에서 화살표함수를 사용하면 this가 외부 컨텍스트를 참조하기 때문에 유용하게 활동할 수 있다.
그런데 콜백함수에 항상 화살표함수가 유용한건 아니다. 밑에 나오지만 addEventListener()에서는 콜백함수에 이벤트 target인 element를 this로 바인딩하기 떄문에 화살표 함수를 사용하면 전역 객체 또는 상위 스코프의 다른 객체를 참조할 수 있다.
콜백함수에 어떤 객체를 this로 바인딩하는지 알아보고 화살표 함수를 사용할지 판단하자.
화살표 함수 사용시 주의할 점
1. 생성자 함수
위에 말한것 처럼 화살표 함수는 프로토타입 속성이 없기 때문에 생성자 함수로 사용할 수 없다.
const Hoony = () => {};
console.log(Hoony.hasOwnProperty('prototype')); // false
const foo = new Hoony(); // TypeError: Foo is not a constructor
2. 객체 메소드
화살표함수는 this를 바인딩하지 않기 때문에 객체 메소드로 선언시 객체를 참조하지 않아 의도한대로 작동하지 않을 수 있다.
const obj = {
name: 'hoony',
fuc1: function () {
console.log(this.name);
},
func2: () => {
console.log(this.name);
}
}
obj.func1(); // hoony
obj.func2(); // TypeError
3. 프로토타입 메소드 지정
2번과 동일한 문제가 발생한다. 객체를 참조하고 싶다면 일반함수를 사용하자
const person = {
name: 'hoony',
};
Object.prototype.sayHi1 = () => console.log(`Hi ${this.name}`);
Object.prototype.sayHi2 = function() {
console.log(`Hi ${this.name}`);
};
person.sayHi1(); // Hi undefined
person.sayHi2(); // Hi hoony
4. addEventListener의 콜백함수
addEventListener는 콜백함수에 이벤트 target 객체를 바인딩한다. 따라서 바인딩을 생략하는 화살표 함수를 사용하면 this가 전역객체를 참조하는 등 의도와 다르게 작동할 수 있다.
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
이처럼 모든 고차함수가 콜백함수에 어떤 객체를 this로 바인딩하는지 잘 알아보고 사용하자
마치며
화살표함수는 간결하고 짧고 빠르게 함수를 작성할 수 있다는 큰 장점이 있지만, 일반함수와 작동 방식에서 차이점이 많아 잘못 사용하면 의도와 다르게 작동하는 리스크를 가지고 있다. 약간 장미, 복어같다는 느낌을 받았다. 실제 프로젝트에 적용해보면서 어떤 상황에서 어떤 함수 선언을 사용하는게 좋은지 나만의 철학을 확립해보자
참고자료
https://hanjungv.github.io/2018-02-03-1_JS_arrow_function/
'자바스크립트 고찰' 카테고리의 다른 글
Node.js 가비지 컬렉션(GC) 이해하기 (1) | 2022.07.24 |
---|