
* 모던 자바스크립트 Deep Dive을 토대로 공부한 것을 정리한 내용으로, 모든 인용문은 모던 자바스크립트 Deep Dive의 문구를 인용한 것입니다.
ES6 이전의 모든 함수는 사용 목적에 따라 명확히 구분되지 않는다. 즉, 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다.
이는 편리해 보일 수 있지만, 사용 목적에 따라 명확한 구분이 없으므로 호출 방식에 특별한 제약이 없고 생성자 함수로 호출되지 않아도 프로토타입 객체를 생성한다. 이는 실수를 유발시킬 수 있으며 성능 면에서도 손해이다.
이러한 문제를 해결하기 위해 ES6에서는 함수를 사용 목적에 따라 세 가지 종류로 구분한다.
| 함수의 구분 | constructor | prototype | super | arguments |
|---|---|---|---|---|
| 일반 함수(Normal) | O | O | X | O |
| 메서드(Method) | X | X | O | O |
| 화살표 함수(Arrow) | X | X | X | X |
ES6 사양에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.
이는 아래 예제에서 확인할 수 있다.
const obj = {
x: 1,
// foo는 메서드이다.
foo() { return this.x; },
// bar에 바인딩된 함수는 메서드가 아닌 일반 함수이다.
bar: function() { return this.x; }
};
console.log(obj.foo()); // 1
console.log(obj.bar()); // 1
new obj.foo(); // -> TypeError: obj.foo is not a constructor
new obj.bar(); // -> bar {}
ES6에서 정의한 메서드는 인스턴스를 생성할 수 없는 non-constructor다. 따라서, ES6 메서드는 생성자 함수로서 호출할 수 없다
위의 예제를 통해 메서드 축약 표현으로 정의된 함수만 메서드임을 확인할 수 있고, 동시에 메서드를 생성자 함수로서 호출할 경우 TypeError를 반환하는 것을 확할 수 있다.
// obj.foo는 constructor가 아닌 ES6 메서드이므로 prototype 프로퍼티가 없다.
obj.foo.hasOwnProperty('prototype'); // -> false
// obj.bar는 constructor인 일반 함수이므로 prototype 프로퍼티가 있다.
obj.bar.hasOwnProperty('prototype'); // -> true
ES6 메서드는 인스턴스를 생성할 수 없으므로 prototype 프로퍼티가 없고 프토로타입도 생성하지 않는다. 때문에 위 예제에서 foo의 hasOwnProperty의 prototype이 false를 반환하는 것을 확인할 수 있다.
const base = {
name: 'Lee',
sayHi() {
return `Hi! ${this.name}`;
}
};
const derived = {
__proto__: base,
// sayHi는 ES6 메서드다. ES6 메서드는 [[HomeObject]]를 갖는다.
// sayHi의 [[HomeObject]]는 sayHi가 바인딩된 객체인 derived를 가리키고
// super는 sayHi의 [[HomeObject]]의 프로토타입인 base를 가리킨다.
sayHi() {
return `${super.sayHi()}. how are you doing?`;
}
};
console.log(derived.sayHi()); // Hi! Lee. how are you doing?
const derived = {
__proto__: base,
// sayHi는 ES6 메서드가 아니다.
// 따라서 sayHi는 [[HomeObject]]를 갖지 않으므로 super 키워드를 사용할 수 없다.
sayHi: function () {
// SyntaxError: 'super' keyword unexpected here
return `${super.sayHi()}. how are you doing?`;
}
};
ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]를 갖는다. supter 함수는 내부 슬롯 [[HomeObject]]를 사용하여 수퍼클래스의 메셔드를 참조하므로 내부 슬롯 [[HomeObject]]를 갖는 ES6 메서드는 super 키워드를 사용할 수 있다.
그러나 ES6 메서드가 아닌 함수는 [[HomeObject]]를 갖지 않기 때문에 super 키워드를 사용할 수 없는 것을 위 예제에서 확인할 수 있다.
super 키워드는 함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드이다.
- super를 호출하면 수퍼클래스의 constructor(super-constructor)를 호출한다.
- super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.
화살표 함수는 function 키워드 대신 화살표(=>)를 사용하여 기존의 함수 정의 방식보다 간략하게 함수를 정의할 수 있다. 화살표 함수는 표현만 간략한 것이 아닌 내부 동작도 기존의 함수보다 간략하다. 특히 화살표 함수는 콜백 함수 내부에서 this가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용하다.
화살표 함수 정의 문법을 예제를 통해 알아보자.
함수 정의
화살표 함수는 함수 표현식으로 정의해야 한다. 함수 선언문으로는 정의할 수 없다.
호출 방식은 기존 방식과 동일하다.
const multiply = (x, y) => x * y;
multiply(2, 3); // -> 6
매개변수 선언
// 매개변수가 여러 개인 경우 소괄호 () 안에 매개변수를 선언한다.
const arrow = (x, y) => { ... };
// 매개변수가 한 개인 경우 소괄호 ()를 생략할 수 있다.
const arrow = x => { ... };
// 매개변수가 없는 경우 소괄호 ()를 생략할 수 없다.
const arrow = () => { ... };
함수 몸체 정의
함수 몸체가 하나의 문으로 구성된다면 함수 몸체를 감싸는 중괄호 {}를 생략할 수 있다. 이때 함수 몸체 내부의 문이 값으로 평가될 수 있는 표현식인 문이라면 암묵적으로 반환된다.
// concise body
const power = x => x ** 2;
power(2); // -> 4
// 위 표현은 다음과 동일하다.
// block body
const power = x => { return x ** 2; };
함수 몸체를 감싸는 중괄호 {}를 생략한 경우 함수 몸체 내부의 문이 표현식이 아닌 문이라면 에러가 발생한다. 표현식이 아닌 문은 반환할 수 없기 때문이다. 이는 아래 예제를 통해 확인할 수 있다.
const arrow = () => const x = 1; // SyntaxError: Unexpected token 'const'
// 위 표현은 다음과 같이 해석된다.
const arrow = () => { return const x = 1; };
// 따라서 함수 몸체가 하나의 문으로 구성되어도 함수 몸체의 문이 표현식이 아닌 문이라면 중괄호를 생략할 수 없다.
const arrow = () => {const x = 1;}
객체 리터럴을 반환하는 경우 객체 리터럴을 소괄호 ()로 감싸 주어야 한다.
const create = (id, content) => ({ id, content });
create(1, 'JavaScript'); // -> {id: 1, content: "JavaScript"}
// 위 표현은 다음과 동일하다.
const create = (id, content) => { return { id, content }; };
// 만약 소괄호로 감싸지 않을 경우 중괄호를 함수 몸체를 감싸는 중괄호로 잘못 해석한다.
// { id, content }를 함수 몸체 내의 쉼표 연산자문으로 해석한다.
const create = (id, content) => { id, content };
create(1, 'JavaScript'); // -> undefined
정안 오빠가 아티클에서 화살표 함수에 관한 내용을 언급한 기억이 있기도 하고, 저번 자바스크립트 과제를 하면서 화살표 함수 관련하여 코드리뷰를 받기도 해서 화살표 함수 내용을 포함한 25장을 정리해봤다.
몰랐던 부분은 아니지만... 이번 과제를 하면서 매개변수가 없는 경우 자꾸 소괄호를 생략해서 오류를 만들었다...... 또 객체 리터럴을 반환할 때도... 소괄호만 감싸고 중괄호 안 감싸서 자꾸자꾸 오류가 생겼다..... 오류가 뜰 때마다 아차 하고 다시 수정하긴 했지만 자꾸 까먹어서 큰일이었는데 이번에 아티클을 정리하면서 앞으로는 헷갈리지 않을 것 같다!! 자바스크립트 기초 스터디 짱,,, 👍
자바스크립트를 공부하면서 든 생각인데 진짜 알면 알수록 깊은 것 같다..... 항상 구현하기에만 급급했던 것 같은데 이렇게 이론을 공부해보니 프로그래밍 언어의 문법이 존재하는 이유는 다 있는 것 같다,,, 열심히 공부해서 이 언어가 왜 이러한 문법을 가지는지 알고 사용하는 사람이 되어야지 🔥
이번에도 아티클이 너무 길어진 느낌인데,,, 개인적으로 화살표 함수 적용하는 걸 쪼금 선호하는 편이라 다른 분들도 화살표 함수 사용법을 자세히 알았으면 좋겠다는 마음에 길어진 거 같다... 🥺
스터디 구성원들도 화살표 함수 사용법을 자세히 알았으면 좋겠다는 마음 잘 받아 이번 화살표 함수 부분 열심히 소화했습니다..!! 항상 신지님 아티클은 자세해서 전체적인 공부를 쭉하고 다시 읽기 너무 좋은 아티클인 것 같아요..! 항상 마지막 마무리 부분 읽으며 해당 범위에 대한 생각을 나누는 기분이 들어 마지막 부분도 항상 뜻깊게 읽고 있습니다! ☺️
화살표 함수에 대해 자세히 적어주신 점 너무너무 좋네요! 저는 화살표 함수가 편리하다! 정도로만 알고 구체적으로 일반 함수와 어떻게 다른지 잘 몰랐거든요. 이번에 자스 기본 공부하면서 처음 알았고, 또 신지님 아티클 읽으면서 차근차근 다시 정리할 수 있어서 너무 좋았습니다 => 💝 진짜 딥다이브 하면 할수록 왜 이걸 사용하고, 언제 이걸 사용하고, 어째서 이게 나왔고!! 이런 내용에 대해 채워지는 것 같아 기초가 탄탄해지는 기분입니다 ㅎ.. 자스 기본 진짜 짱!!! 이번주차 아티클도 깔끔하게 작성해주시느라 넘 고생 많으셨어요! 🤩👍🏻