
ES6 이전까지 자바스크립트의 함수는 별다른 구분없이 다양한 목적으로 사용되었다.
즉, ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수 있다. 다시 말해, ES6 이전의 모든 함수는 callable이면서 constructor다.
이러한 문제를 해결하기 위해 es6에서는 함수를 사용 목적에 따라 세 가지 종류로 명확히 구분하였다.
| ES6 함수의 구분 | constructor | prototype | super | arguments |
|---|---|---|---|---|
| 일반 함수 | O | O | X | O |
| 메서드 | X | X | O | O |
| 화살표 함수 | X | X | X | X |
호출할 수 있는 함수 객체를 callable이라 하며, 인스턴스를 생성할 수 있는 함수 객체를 constructor, 인스턴스를 생성할 수 없는 함수 객체를 non-constructor라고 부른다.
ES6 사양에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.
// ES5
var obj = {
name: 'Lee',
sayHi: function() {
console.log('Hi! ' + this.name);
}
};
// ES6
const obj = {
name: 'Lee',
//sayHi는 메서드이다.(축약표현)
sayHi() {
console.log('Hi! ' + this.name);
}
};
const Foo = () => {};
// 화살표 함수는 생성자 함수로 호출 할 수 없다.
new Foo(); // TypeError : Foo is not constructor
화살표 함수가 일반 함수와 구별되는 가장 큰 특징은 this다.
화살표 함수의 this는 일반 함수의 this에서 발생하는 "콜백 함수 내부의 this 문제", 즉 콜백 함수 내부와 외부의 this가 다르기 때문에 발생하는 문제를 해결하기 위해 의도적으로 설계된 것이다.
this 바인딩은 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다. 이때 주의할 것은 일반 함수로서 호출되는 콜백 함수의 경우다.
class Prefixer {
constructor(prefix) {
this.prefix = prefix;
// this는 생성할 인스턴스에 바인딩
}
add(arr) {
// add 메서드는 인수로 전달된 배열 arr을 순회하며 배열의
// 모든 요소에 prefix를 추가한다.
// ①
return arr.map(function (item) {
// 콜백 함수 내부의 this는 undefined
// 클래스 내부는 strict mode, strict mode에서 일반 함수 호출 시
// 전역 객체가 아니라 undefined에 this 바인딩된다
return this.prefix + item; // ②
// -> TypeError: Cannot read property 'prefix' of undefined
});
}
}
const prefixer = new Prefixer('-webkit-');
console.log(prefixer.add(['transition', 'user-select']));
// 여기서 this 바인딩된다
// 생성자 내부 this는 함수를 호출한 객체(prefixer)에 바인딩된다
즉 위의 코드에서 발생하는 문제가 바로 "콜백 함수 내부의 this 문제"다. 즉, 콜백 함수 this(②)와 외부 함수의 this(①)가 서로 다른 값을 가리키고 있기 때문에 TypeError가 발생한 것이다.
이것을 해결하기 위해 ES6이전에는 다양한 방식으로 해결했었다.
add(arr) {
// this를 일단 회피시킨다.
const that = this;
return arr.map(function (item) {
// this 대신 that을 참조한다.
return that.prefix + ' ' + item;
});
}
add(arr) {
return arr.map(function (item) {
return this.prefix + ' ' + item;
}, this); // this에 바인딩된 값이 콜백 함수 내부의 this에 바인딩된다.
}
add(arr) {
return arr.map(function (item) {
return this.prefix + ' ' + item;
}.bind(this)); // this에 바인딩된 값이 콜백 함수 내부의 this에 바인딩된다.
}
ES6에서는 화살표 함수를 사용하여 문제 해결
add(arr) {
return arr.map(item => this.prefix + item);
}