이미지 출처: vietnamlife.info
화살표 함수(Arrow Function)는 ES6(ECMAScript 2015)에서 도입된 새로운 함수 선언 방식이다. 기존의 함수 선언 방식인 function
키워드 대신 화살표(=>
)를 사용하여 더 짧고 간결하게 함수를 정의할 수 있다.
화살표 함수는 함수 표현식의 대체 방식으로, 함수 선언을 더 간결하게 할 수 있게 해준다. 화살표 함수는 function
키워드를 사용하지 않고, =>
구문을 사용하여 함수를 정의한다. 화살표 함수는 함수 표현식의 한 형태이며, 렉시컬 this를 사용하는 것이 특징이다.
간결한 문법을 제공하지만, 함수 선언문과는 구분된다. 화살표 함수는 this 바인딩이나 arguments 객체를 가지지 않으며, 함수 선언문과 달리 호이스팅되지 않는다.
정리하자면, 화살표 함수는 함수 선언을 간결하게 하고, this
가 동적으로 바뀌지 않고 고정되는 장점이 있다.
const add = (a, b) => a + b;
✏️ 함수 선언문과 함수 표현식에 대한 자세한 설명
JavaScript 함수 선언식 vs 함수 표현식: 차이점과 성능 최적화 전략
this
렉시컬 this는 화살표 함수에서의 this
가 어떻게 동작하는지를 설명하는 개념이다. 여기서 렉시컬이라는 단어는 어휘적이라는 뜻으로, 코드가 작성된 위치에 따라 this
가 고정된다는 의미이다.
쉽게 설명하자면, 화살표 함수에서는 this
가 함수 호출 방식에 따라 달라지지 않고, 함수가 정의된 위치에 있는 this
를 그대로 사용한다. 이를 렉시컬 this라고 부르며, 함수가 호출되는 방식과 상관없이 상위 스코프의 this
를 고정적으로 참조한다.
이를 이해하기 위해, 먼저 전통적인 함수에서 this
가 어떻게 동작하는지 살펴보자.
✏️ 렉시컬 환경에 대한 자세한 설명
[JavaScript] 클로저와 렉시컬 환경 (Closure, Lexical Environment)
일반적인 함수(function
키워드로 선언된 함수)에서 this
는 함수가 호출되는 방식에 따라 달라진다. 호출하는 주체(객체)에 따라 this
가 가리키는 대상이 달라지므로, 같은 함수라도 호출 방법에 따라 다르게 동작할 수 있다.
예시: 전통적인 함수에서의 this
const person = {
name: 'John',
greet: function() {
console.log(this.name); // this는 person 객체를 가리킴
}
};
person.greet(); // "John"
위 코드에서 greet
함수는 객체 person
의 메서드로 호출되었기 때문에 this
는 person
객체를 가리킨다. 그래서 this.name
은 John
이 출력된다.
하지만, 아래와 같이 함수가 독립적으로 호출될 때는 상황이 달라진다.
const greet = person.greet;
greet(); // undefined
이 경우 greet
함수를 객체에서 분리하여 호출했기 때문에 this
는 더 이상 person
객체가 아닌 전역 객체(브라우저에서는 window
)를 가리킨다. 그래서 this.name
은 undefined
가 된다.
화살표 함수에서는 이런 복잡한 this
바인딩 규칙이 없다. 화살표 함수의 this
는 함수가 정의된 시점의 상위 스코프(외부 환경)에서 자동으로 고정된다. 이를 렉시컬 this라고 한다. 즉, 어디서 호출되든 상관없이 this
는 변하지 않고, 함수가 작성된 위치의 this
를 계속 참조한다.
예시: 화살표 함수에서의 this
const person = {
name: 'John',
greet: () => {
console.log(this.name);
}
};
person.greet(); // undefined
여기서 화살표 함수 greet
는 person
객체 내에 정의되었지만, this
는 greet 함수가 정의된 시점의 상위 스코프인 전역 스코프를 참조한다. 따라서 this.name
은 전역 객체의 name
속성을 가리키고, 대부분의 경우 전역 객체에 name
속성이 없으므로 undefined
가 출력된다.
렉시컬 this
의 장점은 특히 비동기 작업이나 콜백 함수에서 두드러진다. 전통적인 함수에서는 this
를 유지하기 위해 bind
나 self = this
같은 패턴을 사용해야 했지만, 화살표 함수는 이런 번거로움이 없다.
const person = {
name: 'John',
greet() {
setTimeout(() => {
console.log(this.name); // this는 person 객체를 가리킴
}, 1000);
}
};
person.greet(); // 1초 후 "John" 출력
위 코드에서 setTimeout
은 비동기 함수이기 때문에, 전통적인 함수라면 this
가 전역 객체로 바뀌는 문제가 발생할 수 있다. 그러나 화살표 함수는 this
가 정의된 시점의 this
를 참조하므로, setTimeout
안에서도 this
는 person
객체를 가리키고, this.name
은 올바르게 John
을 출력한다.
this
바인딩화살표 함수는 전통적인 함수와 달리 this
바인딩을 가지지 않는다. 즉, 화살표 함수 자체의 this
가 존재하지 않으며, 렉시컬 this에 의해 상위 스코프의 this
를 참조하게 된다. 이 특징은 이미 렉시컬 this
설명에서 다루었듯이, 화살표 함수는 호출되는 방식에 관계없이 상위 스코프의 this
를 고정해서 사용한다.
const person = {
name: 'John',
greet: () => {
console.log(this.name); // 전역 객체의 this를 참조
}
};
person.greet(); // undefined
위 코드에서 화살표 함수는 this
가 없다. 따라서 greet
메서드는 전역 스코프의 this
를 참조하고, 결과적으로 this.name
은 undefined
가 출력된다.
arguments
화살표 함수는 this
가 고정된다는 특징 외에도, arguments
객체를 지원하지 않는다. 대신, 나머지 매개변수를 사용해 동일한 기능을 구현할 수 있다.
arguments
객체arguments
객체는 함수가 호출될 때 전달된 모든 인자들을 담고 있는 특별한 객체이다. 하지만 이 arguments
객체는 화살표 함수에서는 사용되지 않는다. 대신, 나머지 매개변수를 사용해야 한다.
arguments
객체란?arguments
객체는 함수가 호출될 때 함수에 전달된 모든 인자들을 배열처럼 저장하는 객체이다. 예를 들어, 함수에 3개의 인자를 전달하면, arguments
객체는 그 3개의 값을 모두 담고 있는 일종의 리스트라고 생각하면 된다.
function sum() {
return Array.from(arguments).reduce((acc, val) => acc + val, 0);
}
console.log(sum(1, 2, 3)); // 6
위 코드를 보면, sum
함수는 인자를 3개 받았고, arguments
객체를 사용해 이 인자들을 모두 더하고 있다. 여기서 중요한 것은, 함수가 몇 개의 인자를 받든 arguments
객체가 자동으로 그 모든 인자를 관리해준다는 것이다.
arguments
객체 대신 나머지 매개변수 사용화살표 함수에서는 arguments
객체를 사용할 수 없다. 그래서 나머지 매개변수라는 방식을 사용해야 한다. 나머지 매개변수는 ...args
와 같은 형식으로 사용하며, 이것도 전달된 모든 인자들을 배열 형태로 관리해준다.
const sum = (...args) => args.reduce((acc, val) => acc + val, 0);
console.log(sum(1, 2, 3)); // 6
위 코드는 화살표 함수에서 ...args
를 사용해 전달된 인자들을 모두 더한 예시이다. arguments
와 달리, 이 방식은 배열처럼 다루기 때문에 훨씬 유연하고 직관적이다.
화살표 함수는 다음과 같은 이유로 자주 사용된다.
화살표 함수는 문법이 간결하여 코드의 가독성을 높여준다. 특히 콜백 함수나 간단한 연산을 처리할 때 유용하다.
// 전통적인 함수 선언
function add(a, b) {
return a + b;
}
// 화살표 함수
const add = (a, b) => a + b;
위의 예시에서 볼 수 있듯이, function
키워드와 return
문을 생략하고 한 줄로 작성할 수 있어 코드가 훨씬 깔끔해진다.
화살표 함수는 this
가 정의된 스코프를 유지하는 렉시컬 this를 가진다. 전통적인 함수에서는 함수 호출 방식에 따라 this
가 동적으로 변경되지만, 화살표 함수는 이를 방지하여 직관적인 this
사용이 가능하다.
function Timer() {
this.seconds = 0;
// 전통적인 함수
setInterval(function() {
this.seconds++; // 오류 발생: this가 setInterval의 컨텍스트를 참조
}, 1000);
}
function TimerFixed() {
this.seconds = 0;
// 화살표 함수
setInterval(() => {
this.seconds++; // 올바르게 작동: this는 TimerFixed 인스턴스를 참조
}, 1000);
}
화살표 함수는 this
를 주변 스코프에서 그대로 가져오기 때문에, bind
메서드나 self = this
같은 패턴을 사용하지 않아도 된다.
매개변수가 하나일 경우 괄호(()
)를 생략할 수 있다.
// 매개변수가 하나일 때 괄호 생략 가능
const square = x => x * x;
매개변수가 없을 때는 반드시 빈 괄호를 사용해야 한다.
const sayHello = () => console.log('Hello!');
본문이 한 줄일 경우 중괄호({}
)와 return
을 생략할 수 있다.
const multiplyByTwo = num => num * 2;
본문이 여러 줄일 경우 중괄호를 사용하며, 명시적으로 return
을 작성해야 한다.
const multiply = (a, b) => {
const result = a * b;
return result;
};
화살표 함수는 new
키워드를 사용하여 생성자로 호출할 수 없다. 이는 화살표 함수가 prototype
속성을 가지고 있지 않기 때문이다.
const Person = (name) => {
this.name = name;
};
// 오류 발생: Person은 생성자가 아님
const person = new Person('John');
생성자로 사용할 함수는 반드시 function
키워드를 사용하여 정의해야 한다.
arguments
객체를 사용할 수 없다화살표 함수는 arguments
객체를 지원하지 않는다. 대신, 필요한 경우에는 나머지 매개변수(rest parameter)를 사용할 수 있다.
// 전통적인 함수에서 arguments 사용
function sum() {
return Array.from(arguments).reduce((total, num) => total + num, 0);
}
// 화살표 함수에서 rest parameter 사용
const sum = (...args) => args.reduce((total, num) => total + num, 0);
화살표 함수는 this
가 고정되어 있어, 객체의 메서드로 사용하기에는 부적합하다. 객체 메서드에서는 function
키워드를 사용하는 것이 더 적합하다.
const person = {
name: 'Alice',
greet: () => {
console.log(`Hello, ${this.name}`); // this는 전역 객체를 참조
}
};
person.greet(); // 'Hello, undefined'
위 코드에서는 this
가 객체를 가리키지 않기 때문에, name
속성에 접근할 수 없다. 객체의 메서드에서는 일반 함수 표현식을 사용하는 것이 올바른 방법이다.
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // [2, 4, 6]
const fetchData = () => {
setTimeout(() => {
console.log('Data fetched');
}, 1000);
};
fetchData(); // 1초 후에 'Data fetched' 출력
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // [2, 4, 6]
document.querySelector('button').addEventListener('click', () => {
console.log('Button clicked');
});
function MyComponent() {
const handleClick = () => {
console.log('Clicked!');
};
return <button onClick={handleClick}>Click me</button>;
}
특성 | 전통적인 함수 | 화살표 함수 |
---|---|---|
this 바인딩 | 호출 방식에 따라 동적으로 변경 | 렉시컬 스코프에 고정된 this |
문법 | function 키워드를 사용 | => 문법을 사용 |
arguments 사용 | 지원 | 미지원 (rest parameter 사용) |
생성자 사용 | 가능 | 불가능 |