ES6 이전까지 자바스크립트의 함수는 별다른 구분 없이 사용되었다.
즉, ES6 이전의 모든 함수는 일반 함수로서 호출할 수 있으면서, 생성자 함수로서 호출할 수 있다.
ES6 에서는 함수를 사용 목적에 따라 세가지 종류로 명확히 구분한다.
ES6 함수의 구분 | constructor | prototype | super | arguments |
---|---|---|---|---|
일반 함수 | O | O | X | O |
메서드 | X | X | O | O |
화살표 함수 | 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
이러한 메서드는 인스턴스를 생성할 수 없는 non-constructor
이다.
인스턴스를 생성할 수 없으므로 prototype
프로퍼티가 없고, 프토토타입도 생성하지 않는다.
new obj.foo(); // -> TypeError: obj.foo is not a constructor
new obj.bar(); // -> bar {}
// obj.foo는 constructor가 아닌 ES6 메서드이므로 prototype 프로퍼티가 없다.
obj.foo.hasOwnProperty('prototype'); // -> false
// obj.bar는 constructor인 일반 함수이므로 prototype 프로퍼티가 있다.
obj.bar.hasOwnProperty('prototype'); // -> true
ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]
를 갖는다.
super
참조는 내부 슬롯 [[HomeObject]]
를 사용하여 수퍼클래스의 메서드를 참조하므로 ES6 메서드는 super
키워드를 사용할 수 있다.
화살표 함수는 function
키워드 대신 화살표를 사용하여 함수를 정의할 수 있다.
몇 가지 주의할 점이 있다.
()
안에 매개변수를 선언한다.()
로 감싸야 한다.화살표 함수의 특징은 다음과 같다.
this
, arguments
, super
, new.target
바인딩을 갖지 않는다.화살표 함수가 일반 함수와 구별되는 가장 큰 특징은 바로 this
다.
화살표 함수는 함수 자체의 this
바인딩이 없고, 상위 스코프의 this
를 그대로 참조한다.
화살표 함수는 다른 함수의 인수로 전달되어 콜백 함수로 사용되는 경우가 많다.
콜백 함수 내부의 this
가 외부 함수의 this
와 다르기 때문에 발생하는 문제, 즉 “콜백 함수 내부의 this
문제” 를 해결하기 위해 의도적으로 설계된 것이다.
class Prefixer {
constructor(prefix) {
this.prefix = prefix;
}
add(arr) {
// add 메서드는 배열 arr 을 순회하며 모든 요소에 prefix를 추가한다.
// 1
return arr.map(function (item) {
return this.prefix + item; // 2
// TypeError 발생
}
}
}
const prefixer = new Prefixer('-webkit-');
console.log(prefixer.add(["transition", "user-select"]);
위 예제를 실행할 때 기대하는 결과는 앞에 -webkit-
이 추가된 배열일 것이다.
하지만 TypeError
가 발생한다.
프로토타입 메서드 내부인 1
번 위치에서의 this
는 메서드를 호출한 객체, 즉 prefixer
객체를 가리킨다. 하지만, map()
메서드의 인수로 전달한 콜백 함수의 내부인 2
번 위치에서의 this
는 undefined
를 가리키게 된다.
22장에서, 일반 함수로서 호출되는 모든 함수 내부의 this 는 전역 객체를 가리킨다고 설명했다.
그런데 클래스 내부의 모든 코드에는 암묵적으로strict mode
가 적용되어 있다.
따라서, 위의map()
메서드의 콜백 함수에도strict mode
가 적용된다.
strict mode
에서 일반 함수로 호출되는 모든 내부 함수의this
는undefined
가 바인딩 된다.
→ 그 결과로,2
번 위치에서의this
가undefined
를 가리키게 된다.
→ 이 문제가 바로 “콜백 함수 내부에서의this
문제” 이다.
화살표 함수는 함수 자체의 this
바인딩을 갖지 않고 화살표 함수 내부에서의 this
는 상위 스코프의 this
를 그대로 참조한다.
→ 이를 lexical this
라 한다. 이는 렉시컬 스코프와 같이 화살표 함수의 this 가 함수가 정의된 위치에 의해 결정된다는 것을 의미한다.
화살표 함수는 함수 자체의 super
바인딩을 갖지 않는다.
->this
와 마찬가지로 화살표 함수 내에서 super
를 참조하면 상위 스코프의 super
를 참조하게 된다.
class Base {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi! ${this.name}`;
}
}
class Derived extends Base {
// 화살표 함수의 super는 상위 스코프인 constructor의 super를 가리킨다.
sayHi = () => `${super.sayHi()}. how are you doing?`;
}
const derived = new Derived('Son');
console.log(derived.sayHi());
//Hi! Son. how are you doing?
화살표 함수는 함수 자체의 arguments
바인딩을 갖지 않는다.
→ 화살표 함수 내부에서 arguments
를 참조하면 this
와 마찬가지로 상위 스코프의 arguments
를 참조한다.
(function() {
// 화살표 함수 foo의 argumetns는 상위 스코프인 즉시 실행 함수의 arguments를 가리킨다.
const foo = () => console.log(arguments);
foo(3, 4);
})(1, 2);
// 전역에는 arguments 객체가 존재하지 않는다.
const foo = () => console.log(arguments);
foo(1, 2); // ReferenceError
매개변수의 이름 앞에 ...
을 붙여 정의한 매겨변수이다.
Rest 파라미터를 통해 전달된 인수들의 목록을 배열로 전달 받을 수 있다.
function foo(...rest) {
// 매개변수 rest는 인수들의 목록을 배열로 전달받는 Rest 파라미터다.
console.log(rest);
}
foo(1, 2, 3, 4, 5);
// [ 1, 2, 3, 4, 5 ]
length
프로퍼티에 영향을 주지 않는다.arguments
객체를 갖지 않는다. 반드시 Rest 파라미터를 사용해야 한다.인수가 전달되지 않은 매개변수의 값은 undefined
이다.
ES6 에서 도입된 매개변수 기본값을 사용하면 이를 방지할 수 있다.
function sum(x = 0, y = 0) {
return x + y;
}
console.log(sum(1, 2)); // 3
console.log(sum(1)); // 1
매개변수 기본값은 매개변수에 인수를 전달하지 않은 경우와, undefined
가 전달된 경우에만 유효하다.
function logName(name = 'Son') {
console.log(name);
}
logName(); // Son
logName(undefined); // Son
logName(null); // null
Rest 파라미터에는 기본값을 지정할 수 없다.
function foo(...rest = []) {
console.log(rest);
}
// SyntaxError