ES6에서 새로 도입된 함수 표현식의 한 형태로 기존 함수 표현식의 function 키워드 대신 화살표 (=>) 를 사용하는 함수이다.
화살표 함수를 사용하면 코드가 간결해지고 콜백 함수나 메서드에서 this의 동작을 명확히 할 수 있는 장점이 있다.
// 기존 함수 표현식
const add = function(num1) {
return num1 + num1;
}
// 화살표 함수
const add = (num1) => {
return num1 + num1;
}
// 매개변수가 하나인 경우 괄호 생략
const add = num1 => {
return num1 + num1;
}
// 함수 본문이 한 줄인 경우 중괄호와 return 생략
const add = num1 => num1 + num1;
함수를 표현하는 방법 중에 하나인 함수 선언문과 함수 표현식을 비교하며 알아보자
일반적인 프로그래밍 언어에서의 함수 선언과 비슷한 형식이다.
function 함수명() {
구현 로직
}
// 예시
function sayHello() {
return ('Hello');
}
sayHello();
유연한 자바스크립트 언어의 특징을 활용한 선언 방식이다.
const 함수명 = function() {
구현 로직
};
// 예시
const sayHello = function() {
return ('Hello');
}
sayHello();
함수 선언문은 호이스팅에 영향을 받지만, 함수 표현식은 호이스팅에 영향을 받지 않는다.
호이스팅이란 변수와 함수 선언이 코드의 최상단으로 끌어올려지는 현상이다.
// 함수 선언문에서의 호이스팅
foo(); // Output: "Hello, world!"
function foo() {
console.log("Hello, world!");
}
함수 선언문의 경우, 함수 선언 전에도 함수를 호출할 수 있다. 이는 함수 선언 전체가 코드의 최상단으로 끌어올려지는 호이스팅 현상 때문이다.
// 함수 표현식에서의 호이스팅
foo(); // TypeError: foo is not a function
var foo = function() {
console.log("Hello, world!");
};
함수 표현식의 경우, 변수 선언(var foo;)은 호이스팅되지만, 함수 할당(foo = function() {...};)은 호이스팅되지 않는다. 따라서 함수를 호출하기 전에 변수 foo는 undefined이므로 TypeError가 발생한다.
함수 선언문의 경우 함수 전체가 호이스팅되어 함수 선언 전에도 호출할 수 있다.
반면 함수 표현식의 경우 변수 선언만 호이스팅되고, 함수 할당은 호이스팅되지 않아 함수 선언 전에는 호출할 수 없다.
호이스팅(Hoisting) 의 개념은 다음 글에서 자세히 다룰 예정이다.
- 화살표 함수에는 'this'가 없다.
// 일반 함수 function normalFunction() { console.log(this); // this는 함수를 호출한 방식에 따라 동적으로 결정됨 } // 화살표 함수 const arrowFunction = () => { console.log(this); // this는 상위 스코프의 this를 가리킴 };화살표 함수는 자신만의
this바인딩을 가지지 않고, 상위 스코프의this를 사용하는데 이를 Lexical this라 한다.
- 화살표 함수에는 'arguments'가 없다.
// 일반 함수 function normalFunction() { console.log(arguments); // arguments 객체에 접근할 수 있음 } // 화살표 함수 const arrowFunction = () => { console.log(arguments); // ReferenceError: arguments is not defined };화살표 함수는 자신만의
arguments객체를 가지지 않는다. 대신 상위 스코프의arguments객체를 사용해야 한다.
- 화살표 함수는 'new'와 함께 호출할 수 없다.
// 일반 함수 function NormalConstructor(name) { this.name = name; } const normalInstance = new NormalConstructor('John'); console.log(normalInstance.name); // 'John' // 화살표 함수 const ArrowConstructor = (name) => { this.name = name; }; const arrowInstance = new ArrowConstructor('Jane'); // TypeError: ArrowConstructor is not a constructor화살표 함수는
this가 없기 때문에 생성자 함수로 사용할 수 없다. 따라서new와 함께 호출할 수 없다.
- 화살표 함수에는 'super'가 없다.
class Rabbit extends Animal { stop() { // 화살표 함수에서 super 사용하려 하면 에러 발생 setTimeout(() => { super.stop(); // Uncaught ReferenceError: super is not defined }, 1000); } }화살표 함수는 생성자 함수로 사용될 수 없기 때문에 화살표 함수 내부에서는
super를 직접 사용할 수 없다.class Rabbit extends Animal { stop() { // 일반 함수에서 super 사용 setTimeout(function() { super.stop(); // 정상 작동 }, 1000); } }이를 해결하려면 화살표 함수 외부의 일반 함수에서
super를 가져와야 한다.
이 경우 setTimeout() 내부의 일반 함수에서 super.stop()을 호출할 수 있다. 이렇게 하면 부모 클래스인 Animal의 stop() 메서드를 호출할 수 있다.
또 다른 방법으로는 화살표 함수 외부에서 super를 가져와서 사용하는 것이다.class Rabbit extends Animal { stop() { // 일반 함수에서 super 사용 setTimeout(function() { super.stop(); // 정상 작동 }, 1000); } }이 경우 stop() 메서드 내부에서 super.stop을 호출하여 부모 클래스의 stop() 메서드를 가져온 다음, 화살표 함수 내부에서 call(this)를 사용하여 부모 클래스의 메서드를 호출할 수 있다.
화살표 함수로 메소드를 정의하는 것은 피해야 한다.
// Bad
const person = {
name: 'Lee',
sayHi: () => console.log(`Hi ${this.name}`)
};
person.sayHi();
위 예제의 경우, 메소드로 정의한 화살표 함수의 내부의 this는 메소드를 소유한 객체, 즉 메소드를 호출한 객체를 가리키지 않고 상위 컨텍스트인 전역 객체 window를 가리킨다. 따라서 화살표 함수로 메소드를 정의하는 것은 바람직하지 않다.
// Good
const person = {
name: 'Lee',
sayHi() { // === sayHi: function() { // ES6 메소드 축약 표현
console.log(`Hi ${this.name}`);
}
};
person.sayHi(); // Hi Lee
이와 같은 경우는 메소드를 위한 단축 표기법인 ES6의 축약 메소드 표현을 사용하는 것이 좋다.
화살표 함수로 정의된 메소드를 prototype에 할당하는 경우도 동일한 문제를 발생한다.
// Bad
const person = {
name: 'Lee',
};
Object.prototype.sayHi = () => console.log(`Hi ${this.name}`);
person.sayHi(); // Hi undefined
화살표 함수로 객체의 메소드를 정의하였을 때와 같은 문제가 발생한다. 따라서 prototype에 메소드를 할당하는 경우, 일반 함수를 할당한다.
// Good
const person = {
name: 'Lee',
};
Object.prototype.sayHi = function() {
console.log(`Hi ${this.name}`);
};
person.sayHi(); // Hi Lee
화살표 함수는 생성자 함수로 사용할 수 없다.
const Foo = () => {};
// 화살표 함수는 prototype 프로퍼티가 없다
console.log(Foo.hasOwnProperty('prototype')); // false
const foo = new Foo(); // TypeError: Foo is not a constructor
생성자 함수는 prototype 프로퍼티를 가지며 prototype 프로퍼티가 가리키는 프로토타입 객체의 constructor를 사용한다. 하지만 화살표 함수는 prototype 프로퍼티를 가지고 있지 않다.
addEventListener 함수의 콜백 함수를 화살표 함수로 정의하면 this가 상위 컨택스트인 전역 객체 window를 가리킨다.
// Bad
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
따라서 addEventListener 함수의 콜백 함수 내에서 this를 사용하는 경우, function 키워드로 정의한 일반 함수를 사용하여야 한다. 일반 함수로 정의된 addEventListener 함수의 콜백 함수 내부의 this는 이벤트 리스너에 바인딩된 요소(currentTarget)를 가리킨다.
// Good
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
https://ko.javascript.info/arrow-functions
https://joshua1988.github.io/web-development/javascript/function-expressions-vs-declarations/#%ED%95%A8%EC%88%98-%EC%84%A0%EC%96%B8%EB%AC%B8---function-declarations
다음 글은 앞서 언급되었던 호이스팅 개념을 정리하여 작성해보겠습니다.