화살표 함수(arrow function)는 JavaScript에서 도입된 새로운 함수 선언 방법이다. ES6(ECMAScript 2015)에서 처음 도입되었으며, 기존의 함수 선언 방식에 비해 더 간결하고 가독성이 높은 코드를 작성할 수 있게 해준다. 다음은 화살표 함수에 대한 주요 특징과 사용 예제이다.
=>
기호를 사용하여 정의된다.this
컨텍스트를 가지지 않고, 자신을 감싸고 있는 외부 함수 또는 객체의 this
를 그대로 사용합니다. 이로 인해 this와 관련된 오류를 줄일 수 있다.// 매개변수가 하나인 경우
const square = x => x * 2;
console.log(square(5)); // 10
// 매개변수가 여러개인 경우
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
// 매개변수가 없는 경우
const greet = () => 'Hello, World!';
console.log(greet()); // 'Hello, World!'
// 블록바디 사용
// 화살표 함수의 함수 본문이 여러 줄인 경우, 중괄호 {}를 사용하고 return 키워드를 명시해야 한다.
const multiply = (a, b) => {
const result = a * b;
return result;
};
console.log(multiply(3, 4)); // 12
// 객체 반환
// 객체를 반환할 때는 객체 리터럴을 괄호로 감싸야 한다.
const getPerson = (name, age) => ({ name, age });
console.log(getPerson('Alice', 25)); // { name: 'Alice', age: 25 }
// 일반 함수로 바꾸면 다음과 같다.
function getPerson(name, age) {
return { name, age };
}
JavaScript에서 this 키워드는 함수가 호출된 방식에 따라 달라지는 동적 바인딩이다. 그러나 화살표 함수는 자신만의 this 바인딩을 가지지 않고, 외부 컨텍스트의 this를 상속받는다. 이는 화살표 함수의 중요한 특징 중 하나로, 특히 콜백 함수나 비동기 코드에서 유용하다.
출처
화살표 함수와 this를 자세히 설명해주는 벨로그!! 이곳의 설명을 참조하고 제 나름대로 정리해봤습니다.
Java를 사용하던 나로서는, JS의 this를 접했을 때, 혼란스러움이 생겼다.
function Person(name) {
this.name = name;
}
const person1 = new Person('Alice');
console.log(person1.name); // Alice
// 일반 함수의 `this`
function sayName() {
console.log(this.name);
}
const person2 = {
name: 'Bob',
sayName: sayName
};
person2.sayName(); // Bob
const unboundSayName = person2.sayName;
unboundSayName(); // undefined (또는 엄격 모드에서는 에러)
왜 unboundSayName
이 undefined
로 출력되는 것일까?
이는 바로 this
의 컨텍스트 의존성 때문이다.
JavaScript에서 함수 내의 this 키워드는 함수가 어떻게 호출되었는지에 따라 다르게 바인딩된다. 일반 함수(화살표 함수 제외)의 경우, this는 함수를 호출하는 객체에 바인딩된다. 하지만 함수를 다른 변수에 할당할 경우 그 함수를 담은 변수는 원래 객체와의 연결이 끊어진 상태로 존재하게 된다. 따라서 그 변수를 통해 함수를 호출하면 this는 전역 객체(브라우저에서는 window, Node.js에서는 global)나, 엄격 모드('strict mode')에서는 undefined에 바인딩된다.
JS의 this는 상황에 따라 다르게 바인딩 되는데, 대표적으로 this에 바인딩되는 값들은 아래와 같다.
3번에 대해서 주의깊게 봐야 하는데, 함수를 호출 했을 때 그 함수 내부의 this는 지정되지 않는다. 그리고, this가 지정되지 않은 경우, this는 자동으로 전역 객체를 바라보기 때문에 함수를 호출하면, 함수 내부에서의 this는 전역객체가 된다고 할 수 있다.
이 부분은 자바스크립트 개발자 중 한명인 더글라스 크락포드도 설계상 오류라고 지적하였다...
const cat = {
name: 'meow',
foo1: function() {
const foo2 = function() {
console.log(this.name);
}
foo2();
}
};
cat.foo1(); // undefined
cat.foo1()
메소드 호출 시 내부 함수 foo2
가 실행됨foo2
내부의 this
는 지정되지 않아서 곧 전역 객체를 가리킴!!this.name
은 cat
객체의 내부에 지정한 meow
일 줄 알았는데 undefined
가 나왔다. 함수를 호출할 때, 그 함수를 호출한 객체의 this
가 나오도록 설계했지만... 함수를 호출 했을때, 그 함수 내부의 this는 지정되지 않으므로 실패한 것이다.
하지만 화살표 함수는 위와 같은 상황을 깔끔하게 해결해준다.
const cat = {
name: 'meow',
foo1: function() {
const foo2 = () => {
console.log(this.name);
}
foo2();
}
};
cat.foo1(); // meow
화살표 함수에서 this
가 존재하지 않는다.
이게 가능한 이유는 화살표 함수에는 this가 아예 없다. 즉, function으로 선언한 함수를 실행할 땐 this가 존재하긴 하지만 값을 지정하지 않는데, 화살표 함수로 선언한 함수에는 this가 없다.
상세하게 설명하자면, 여기서 this
가 존재하지 않는다고 말하는 것은, 화살표 함수가 자신만의 this
바인딩을 생성하지 않는다는 의미이다. 즉, 화살표 함수 내부에서 사용되는 this
는 함수가 작성된 렉시컬 환경(lexical context)의 this
를 그대로 상속받는다. 이는 화살표 함수의 중요한 특징 중 하나로, 여러 상황에서 유용하게 사용된다.
this
란...this
JavaScript에서는 어떤 식별자(변수)를 찾을 때 현재 환경에서 그 변수가 없으면 바로 상위 환경을 검색한다. 그렇게 점점 상위 환경으로 타고 타고 올라가다가 변수를 찾거나 가장 상위 환경에 도달하면 그만두게 되는 것다. 화살표 함수에서의 this 바인딩 방식도 이와 유사하다. 화살표 함수에는
this
라는 변수 자체가 존재하지 않기 때문에 그 상위 환경에서의 this를 참조하게 된다.
JS에서 화살표 함수의 this
바인딩 방식은 변수가 스코프 체인을 통해 검색되는 방식과 유사하게 작동한다. 이를 렉시컬 스코핑(lexical scoping) 또는 정적 스코핑(static scoping)이라고 한다. 화살표 함수의 경우, this는 함수가 정의된 시점의 렉시컬 환경에서 결정된다.
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // 여기서 `this`는 Person 인스턴스를 가리킴
console.log(this.age);
}, 1000);
}
const p = new Person(); // `this.age` 값이 1초마다 증가
이 예제에서 setInterval
내의 화살표 함수는 Person 생성자 함수의 this
를 상속받습니다. 일반 함수를 사용했다면, setInterval
은 일반적으로 this
를 window
(브라우저 환경)나 undefined
(엄격 모드)로 설정하므로, this.age
에 접근할 수 없게 된다. 화살표 함수 덕분에 Person 객체의 age 속성에 접근하고 조작할 수 있다.
1.메소드
const cat = {
name: 'meow',
callName: () => console.log(this.name)
}
cat.callName(); // undefined
이 같은 경우, callName
메소드의 this
는 자신을 호출한 객체 cat
이 아니라 함수 선언 시점의 상위 스코프인 전역객체를 가리키게 된다. 어차피 일반 함수를 사용해도 메소드로 호출하면 자신을 호출한 객체를 가리키기 때문에 메소드에서 화살표 함수를 쓸 필요는 없다. 위 함수를 옳게 바꾸면 다음과 같다.
const cat = {
name: 'meow',
callName: function() {
console.log(this.name);
}
};
cat.callName(); // "meow"
이 예제에서는 callName
메소드를 일반 함수로 정의했다. 일반 함수에서는 this
가 해당 함수를 호출하는 객체에 동적으로 바인딩되기 때문에, cat
객체의 callName
메소드를 호출할 때 this
는 cat
객체를 가리키고 this.name
은 'meow'를 정상적으로 출력한다.
또 다른 방법으로, ES6의 짧은 메소드 구문을 사용하여 더 간결하게 표현할 수 있다
const cat = {
name: 'meow',
callName() {
console.log(this.name);
}
};
cat.callName(); // "meow"
이 방법은 함수를 정의할 때 function 키워드를 생략하고, 객체 리터럴 내에서 메소드를 정의하는 직관적인 방법이다. 이 경우에도 this는 메소드가 호출된 객체인 cat을 가리킨다.
2.생성자 함수
const Foo = () => {};
const foo = new Foo() // TypeError: Foo is not a constructor
애초에 생성자 함수로 사용할 수 없게 제작되었다.
3.addEventListener()의 콜백함수
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this); // Window
this.innerHTML = 'clicked';
});
button.addEventListener('click', function() {
console.log(this); // button 엘리먼트
this.innerHTML = 'clicked';
});
원래 addEventListener
의 콜백함수에서는 this
에 해당 이벤트 리스너가 호출된 엘리먼트가 바인딩되도록 정의되어 있다. 이처럼 이미 this의 값이 정해져있는 콜백함수의 경우, 화살표 함수를 사용하면 기존 바인딩 값이 사라지고 상위 스코프(이 경우엔 전역 객체)가 바인딩되기 때문에 의도했던대로 동작하지 않을 수 있다. 물론 상위 스코프의 속성들을 쓰려고 의도한 경우라면 사용할 수 있다.
this
를 가지지 않습니다. 대신 정의된 위치의 스코프에서 this
를 상속받는다.this
가 일관되게 유지되어야 하는 경우 (예: 콜백 함수, 타이머, 이벤트 핸들러 등) 매우 유용하다.this
바인딩 관련 일반적인 오류를 줄일 수 있다.이러한 이유로 화살표 함수는 특히 JavaScript의 비동기 코드나 콜백 패턴에서 매우 유용하게 사용된다.
자바스크립트에서 일반 함수와 화살표 함수가
arguments
객체를 다루는 방식에는 차이가 있다.arguments
객체는 함수로 전달된 모든 인수를 배열 형태로 제공하는 유사 배열 객체이다. 일반 함수에서는arguments
객체를 사용할 수 있지만, 화살표 함수에서는 사용할 수 없다.
일반 함수에서는 arguments
객체를 사용할 수 있다.
function regularFunction() {
console.log(arguments);
console.log('Number of arguments:', arguments.length);
for (let i = 0; i < arguments.length; i++) {
console.log(`Argument ${i}:`, arguments[i]);
}
}
regularFunction(1, 'two', true, null, { key: 'value' });
화살표 함수에서는 arguments 객체를 사용할 수 없다. 대신, rest parameters를 사용할 수 있다.
자바스크립트의
Rest Parameters
는 함수에 전달된 인수들을 배열로 다루기 위한 문법이다. 이 문법을 사용하면 함수가 받는 인수의 수가 고정되지 않고, 동적으로 처리할 수 있다. 이는 함수가 정해지지 않은 수의 인수를 받아야 할 때 매우 유용하다.
Rest Parameters
는 이름 앞에 세 개의 점(...)을 붙여서 사용한다. 이 문법은 함수의 마지막 매개변수로 사용되어야 하며, 그 함수로 전달된 나머지 모든 인수를 배열로 모아준다.
Rest parameters
로 받은 인수는 배열이므로, forEach, map, reduce 등의 배열 메소드를 직접 사용할 수 있다.arguments
객체보다 훨씬 직관적이고 편리하다. arguments
객체는 유사 배열 객체이므로, 배열 메소드를 직접 사용할 수 없는 반면, Rest parameters
는 진정한 배열이다.const arrowFunction = (...args) => {
console.log(args);
console.log('Number of arguments:', args.length);
args.forEach((arg, index) => {
console.log(`Argument ${index}:`, arg);
});
}
arrowFunction(1, 'two', true, null, { key: 'value' });
function regularFunction() {
const arrowFunction = () => {
console.log(arguments);
};
arrowFunction();
}
regularFunction(1, 2, 3); // [1, 2, 3]
위 예에서 regularFunction의 arguments 객체는 arrowFunction에서 접근 가능하다. 그러나 이는 화살표 함수가 자체적으로 arguments를 가지고 있기 때문이 아니라, regularFunction의 arguments를 렉시컬 스코프를 통해 "상속"받기 때문이다.
arguments
객체를 사용할 수 있으며, 이는 함수 내에서 사용할 수 있는 유사 배열 객체이다.arguments
객체를 사용할 수 없고, 대신 rest parameters
를 사용하여 함수 인수를 배열로 받을 수 있다.화살표 함수는 설계상 자신의 arguments 객체를 생성하지 않고, 필요한 경우 렉시컬 상위 스코프의 arguments를 참조하거나, 보다 명확하고 예측 가능한 나머지 매개변수를 사용하여 인수를 처리한다. 이는 코드를 더 간결하고 이해하기 쉽게 만들어 준다.