모던 자바스크립트 Deep Dive 정리
1. 함수의 구분
▶ ES6 이전에는 일반 함수, 생성자 함수, 메서드, 콜백 함수 간 구분이 명확하지 않았다
▷ 예) new 만 붙이면 생성자 함수
▷ 모든 함수가 callable(호출할 수 있는 함수 객체)이면서 constructor(인스턴스 생성 가능한 함수 객체)
▷ 이는 실수를 유발하고 성능을 저해함
▷ constructor는 매번 prototype 프로퍼티를 가지고, 프로토타입 객체도 만들기 때문
▶ 그래서 ES6에서는 함수를 사용 목적에 따라 4가지로 명확히 구분한다
ES6 함수의 구분 | constructor | prototype | super | arguments |
일반 함수 | O | O | X | O |
메서드 | X | X | O | O |
화살표 함수 | X | X | X | X |
▶ 표준 빌트인 객체가 제공하는 프로토타입 메서드와 정적 메서드는 모두 non-constructor
▷ prototype 프로퍼티가 없고, 프로토타입도 생성하지 않음
2. 메서드
▶ ES6 이전 일반적인 메서드의 정의 : 객체에 바인딩된 함수
▶ ES6에서의 메서드의 정의 : 메서드 축약 표현으로 정의된 함수만을 의미
▶ ES6 메서드는 내부 슬롯 [[HomeObject]]를 갖는다
▷ 자신을 바인딩한 객체를 가리킴
▶ super 참조는 [[HomeObject]] 를 사용하여 수퍼클래스의 메서드를 참조하는 것
▷ ES6 메서드가 아닌 함수는 super 키워드를 사용할 수 없다
▷ super : [[HomeObject]]의 프로토타입(상위 객체)을 지칭
3. 화살표 함수
3-1. 화살표 함수 정의
▶ 매개변수가 한 개인 경우 소괄호 ()를 생략 가능
▶ 함수 몸체가 하나의 문으로 구성된다면 함수 몸체를 감싸는 중괄호 {}를 생략 가능
// concise body
const power = x => x ** 2;
power(2); // -> 4
// 위 표현은 다음과 동일하다.
// block body
const power = x => { return x ** 2; };
▶ 객체 리터럴을 반환하는 경우 객체 리터럴을 소괄호 ()으로 감싸주어야 한다
const create = (id, content) => ({ id, content });
// 위 표현은 다음과 동일하다
const create = (id, content) => { return { id, content }; };
▶ 화살표 함수도 즉시실행함수로 사용할 수 있다
const person = (name => ({
sayHi() { return `Hi? My name is ${name}.`; }
}))('Lee');
console.log(person.sayHi()); // Hi? My name is Lee.
3-2. 화살표 함수와 일반 함수의 차이
▶ 화살표 함수는 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않는다
▷ 따라서 화살표 함수 내부에서 상기한 바인딩을 참조하면 스코프 체인을 통해 상위 스코프의 바인딩을 참조한다
3-3. this
▶ strict mode 에서는 일반 함수로 호출된 모든 함수 내부의 this에는 전역 객체가 아닌 undefined가 바인딩된다
▷ 클래스 내부에서는 암묵적으로 strict mode가 적용되므로, map() 같은 고차 함수의 콜백 함수로 일반 함수 형태를 사용하면 undefined가 바인딩된다.
▶ 이를 해결하기 위해 ES6에서 도입된 화살표 함수를 사용하면 된다
▷ 화살표 함수는 함수 자체의 this 바인딩이 없다
▷ 따라서 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다
▷ 이를 lexical this 라고 한다 (lexical scope처럼 this가 함수가 정의된 위치에 의해 결정되는 것)
▶ 만약 화살표 함수가 전역에서 사용되면 this에는 전역 객체가 바인딩된다
▶ 화살표 함수는 함수 자체의 this 바인딩을 갖지 않기 때문에, Function.prototype.call, apply, bind 메서드를 사용해도 화살표 함수 내부의 this를 바꿀 수 없다
▷ 언제나 상위 스코프의 this 바인딩을 참조한다
▶ 이러한 이유로 메서드를 화살표 함수를 정의해선 안된다
▷ 또한 프로토타입 객체의 프로퍼티에 화살표 함수를 할당해도 안된다
3-4. super
▶ this와 마찬가지로 상위 스코프의 super를 참조한다
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi! ${this.name}`;
}
}
class Derived extends Base {
// 화살표 함수의 super는 상위 스코프인 constructor의 super를 가리킨다.
// constructor가 생략되었지만, 암묵적으로 생성됐다
sayHi = () => `${super.sayHi()} how are you doing?`;
}
const derived = new Derived('Lee');
console.log(derived.sayHi()); // Hi! Lee how are you doing?
▷ 내부 슬롯 [[HomeObject]] 를 갖는 ES6 메서드 내에서만 사용 가능
3-5. arguments
▶ 마찬가지로 상위 스코프의 arguments를 참조
▷ 전역에는 arguments 객체가 존재하지 않는다
4. Rest 파라미터
기본 문법
▶ 함수에 전달된 인수들의 목록을 배열로 전달받는다
▷ Rest 파라미터는 하나만 선언할 수 있고, 반드시 마지막 매개변수이어야 한다
▶ 함수 정의 시 선언한 매개변수 갯수를 나타내는 함수 객체의 length 프로퍼티에 영향을 주지 않는다
function foo(...rest) {}
console.log(foo.length); // 0
function bar(x, ...rest) {}
console.log(bar.length); // 1
function baz(x, y, ...rest) {}
console.log(baz.length); // 2
▶ arguments 객체를 보완하기 위해 등장
▷ arguments 객체는 유사배열이므로 순회하려면 call이나 apply 메서드를 통해 배열로 변환하는 번거로움이 있었다
▷ 화살표 함수는 arguments 객체를 갖지 않기 때문에 Rest 파라미터를 사용해야 한다
5. 매개변수 기본값
▶ 매개변수에 기본값을 전달할 수 있다
▷ 자바스크립트에서는 인수누락, 초과 전달 등을 체크하지 않기 때문에, 실수로 인한 에러를 방지할 수 있다
▶ 매개변수 기본값은 매개변수에 인수를 저달하지 않은 경우와 undefined를 전달한 경우에만 유효하다
function logName(name = 'Lee') {
console.log(name);
}
logName(); // Lee
logName(undefined); // Lee
logName(null); // null