ES6 이전 자바스크립트의 함수는 구분 없이 다양한 목적으로 사용되었다
자바스크립트의 함수는 일반적인 함수로서 호출할 수도 있고
new 연산자와 함께 호출하여 인스턴스를 생성할 수 있는 생성자 함수로서 호출할 수도 있으며
객체에 바인딩되어 메서드로서 호출할 수도 있다
이는 편리하지만 실수를 유발할 수 있다
ES6 이전 모든 함수는 일반함수 + 생성자 함수로의 기능
callable 이면서 constructor 다
ES6 이전 메서드라고 부르던 객체에 바인딩된 함수도
callable 이며 constructor 다
이는 문법상 가능한 것으로 문제가 있고 성능에도 좋지 않다
메서드나 콜백 함수도 constructor 이기 때문에
불필요한 프로토타입 객체를 생성한다
ES6 부터는 이러한 것을 해결하기 위해 세 가지로 함수를 구분한다
일반 함수는 함수 선언문이나 표현식으로 작성한 함수
화살표 함수는 non-cunstructor 다
ES6 이전 사양에서는 메서드에 대한 정의가 없었다
객체에 바인딩 된 함수르 메서드라고 불렀다
ES6 사양에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미
ES6 메서드는 인스턴스를 생성할 수 없는 non-constructor 다
화살표 함수나 메서드는 non-constructor 이기 때문에
프로토타입을 생성하지 않고 prototype 프로퍼티도 가지지 않는다
표준 필트인 객체가 제공하는 프로토타입 메서드와 정적 메서드는 모두 non-constructor 다
ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]] 를 갖는다
super 참조는 내부 슬롯 [[HomeObject]] 를 사용하여 수퍼 클래스의 메서드를 참조하므로 메서드는 super 키워드를 사용할 수 있다
메서드가 아닌 함수는 super 키워드를 사용할 수 없다
화살표 함수는 기존 함수 정의 방식보다 간략하게 함수를 정의할 수 있다
표현만 간단한 것이 아니라 내부 동작도 간략하다
특히 화살표 함수는 콜백 함수 내부에서 this 가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용하다
화살표 함수도 즉시 실행 함수로 사용 가능
화살표 함수도 일급 객체이므로 고차 함수에 인수로 전달할 수 있다
화살표 함수와 일반 함수가 구별되는 가장 큰 특징은 this
화살표 함수는 다른 함수의 인수로 전달되어 콜백으로 사용되는 경우가 많다
화살표 함수는 일반 함수 내부의 this 문제를 해결하는데 사용하기 좋다
콜백 함수도 일반 함수로 작성하면 내부 this 애 전역 객체 혹은 undefined 가 바인딩 된다
고차 함수의 인수로 전달되는 콜백 함수도 중첩 함수이기 때문이다
화살표 함수는 함수 자체의 this 바인딩을 갖지 않기 때문에 화살표 함수 내부에서 this 를 참조하면 상위 스코프의 this 를 그대로 참조한다
이를 lexical this 라고 한다
렉시컬 스코프와 같이 화살표 함수의 this 가 함수가 정의된 위치에 의해 결정되는 것을 의미한다
화살표 함수를 제외한 모든 함수에는 this 바인딩이 존재한다
화살표 함수가 도입되기 이전에는 일반적인 식별자처럼 스코프 체인을 통해
this 를 탐색할 필요가 없었다
화살표 함수는 this 바인딩이 존재하지 않기 때문에 스코프 체인을 통해 상위 스코프에서 this 를 탐색하게 된다
화살표 함수가 중첩되어 있다면 마찬가지로 중첩된 최상위 함수의 상위 스코프의 this 를 참조하게 된다
프로퍼티에 할당한 화살표 함수도 스코프 체인 상에서 가장 가까운 상위 함수의 this 를 참조하게 된다
화살표함수는 this 바인딩을 갖지 않기 때문에 call, apply, bind 메서드를 사용해도 내부의 this 를 교체할 수 없다
각각 메서드를 호출할 수는 있지만 this 가 언제나 상위 스코프의 this 를 참조하게 된다
일반적인 메서드도 화살표 함수로 작성하는 것은 피해야 한다
const Person = {
name: 'Lee',
sayHi : () => console.log(`hi ${this.name}`)
}
위 예제에서 sayHi 메서드 내부 this 는 Person 객체를 가리키지 않고
상위 스코프인 전역의 this 가 가리키는 전역 객체를 가리키게 된다
프로토타입 객체의 프로퍼티에 함수를 할당하는 경우에도 동일한 문제가 발생한다
프로퍼티를 동적 추가할 때는 ES6 메서드 정의를 사용할 수 없으므로 일반 함수를 할당한다
ES6 메서드를 동적 추가하고 싶다면 객체 리터럴을 바인딩하고 프로토타입의 constructor 프로퍼티와 생성자 함수 간의 연결을 재설정해야 한다
function Person(name) {
this.name = name
}
Person.prototype = {
constructor: Person,
sayHi () {console.log(`${this.name} hi`)}
}
const person = new Person('Lee')
person.sayHi()
클래스 필드에 화살표 함수를 할당할 수 있다
// bad
class Person {
name = 'Lee'
sayHi = () => console.log(`${this.name} hi`)
}
이 때 화살표 함수로 작성된 클래스 필드 내부에서의 this 는 상위 스코프를 가리킨다
클래스 필드의 상위 스코프는 constructor 다
따라서 화살표 내부에서 참조한 this 는 constructor 내부의 this 바인딩과 같다
constructor 내부의 this 바인딩은 클래스가 생성한 인스턴스를 가리키므로
위 화살표 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 된다
따라서 메서드를 정의할 때는 메서드 축약 표현이 좋다
// 위 bad 코드와 같음
class Person {
constrcutor () {
this.name = 'Lee'
this.sayHi = () => console.log(`${this.name} hi`)
}
}
화살표 함수는 함수 자체의 super 바인딩을 갖지 않는다
따라서 상위 스코프의 super 를 참조하게 된다
super 는 내부 슬롯 [[HomeObject]] 를 갖는 ES6 메서드 내에서만 사용할 수 있다
클래스 내부에서 화살표 함수로 작성된 인스턴스 메서드의 super 는 상위 스코프의 super 가 바인딩 된다
화살표 함수는 arguments 바인딩을 가지지 않기 때문에 상위 스코프의 arguments 를 참조하게 된다
화살표 함수로 가변 인자 함수를 구현해야 할 때는 반드시 Rest 파라미터 문법을 사용한다
가변 인자 인수들의 목록을 배열 형식으로 전달받는다
일반 매개변수와 함께 사용하고 Rest 파라미터는 이름과 같이
나머지, 마지막 파라미터여야 한다
Rest 파라미터는 하나만 사용할 수 있다
function foo(a, ...rest) {
console.log(a) // 1
console.log(rest) // [2, 3]
}
foo(1, 2, 3)
arguments 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회 가능한
유사 배열 객체며 함수 내부에서 지역 변수처럼 사용할 수 있다
하지만 유사 배열 객체이므로 배열 메서드 사용이 번거롭다
함수 호출 시 매개변수 개수만큼 인수를 전달하는 것이 바람직하지만
그렇지 않아도 에러가 발생하지는 않는다
자바스크립트 엔진은 매개변수 개수와 인수의 개수를 체크하지 않음
전달되지 않은 인수는 undefined, 초과된 인수는 무시된다