✔️ ES6 이전의 모든 함수는 일반 함수로도 호출할 수도 있고, 생성자 함수로서 호출할 수 있다. (callable & constructor)
var foo = function(){
return 1;
};
//일반적인 함수로서 호출
foo(); // 1
//생성자 함수로서 호출
new foo(); // foo{}
//메서드로서 호출
var obj = {foo :foo};
obj.foo(); //1
🔖 참고)
여기서 발생하는 문제 👉
객체에 바인딩된 함수도 일반 함수로서 호출할 수 있는 것은 물론 생성자 함수로서 호출할 수도 있음, 콜백함수도 constructor이기 때문에 불필요한 프로토타입 객체를 생성하는 문제 등
이를 해결하고자 ES6에서는, 함수를 세가지 종류로 구분한다.
- 일반함수 : constructor, prototype, arguments
- 메서드 (Method) : super, arguments
- 화살표 함수(Arrow)
ES6 사양에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.
✔️ ES6사양에서 정의한 메서드는 인스턴스를 생성할 수 없는 non-constructor 이다.
✔️ ES6 메서드는 prototype 프로퍼티가 없고 프로토타입도 생성하지 않음
✔️ ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯[[HomeObject]]
를 갖는다. (super 키워드 사용 가능)
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 {}
obj.foo.hasOwnProperty('prototype'); // false
✔️ 참고로 표준 빌드인 객체가 제공하는 프로토타입 메서드와 정적 메서드는 모두 non-constructor 이다.
String.prototype.toUppercase.prototype; // undefined
✔️ ES6 메소드의 super 키워드 예시
const base = {
name : 'Lee',
sayHi() {
return `Hi ${this.name}`;
}
}
const derived = {
__proto__ : base,
sayHi(){
return `${super.sayHi()}`
}
};
console.log(derived.sayHi());// Hi Lee
// 함수 정의
const multiply = (x, y) => x * y;
// 매개변수 선언 - 소괄호 생략 가능(매개변수가 한개인 경우)
const arrow = x => {...};
// 매가변수 없는 경우 소괄호 생략 불가능
const arrow = () => {...};
// 함수 몸체 정의 - 중괄호 생략 가능
const power = x => x * 2;
//객체 리터럴을 반환하는 경우 객체리터럴을 소괄호로 감싸주어야 한다.
cosnt create = (id, create) => ({id, create});
// 함수 몸체가 여러개의 문으로 구성되어있는 경우 중괄호 생략 불가능
const sum = (a,b) => {
const result = a+b;
return result;
}
// 화살표 함수도 즉시 실행 함수로 사용 가능
const person = (name => ({
sayHi(){return `Hi? My name is ${name}.`;}
}))('Lee');
console.log(person.sayHi());// Hi? My name is Lee.
✔️ 화살표 함수는 인스턴스를 생성할 수 없는 non-constructor다.
✔️ 중복된 매개변수 이름을 선언할 수 없다.const arrow =(a,a) => a + a // SyntaxError
✔️ 화살표 함수는 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않는다.
✔️ ES6에서는 화살표 함수를 사용하여 "콜백 함수 내부의 this 문제를 해결할 수 있다.
👉 map 을 통해 일반함수를 return할 때 this는 global 객체를 가리키게 되고 class 내부에서는 코드가 암묵적으로 'strict mode'로 동작하기 때문에 'undefined'를 반환한다는 문제를 갖고 있었음.
class Prefixer {
constructor(prefix){
this.prefix = prefix;
}
add(arr){
return arr.map(item => this.prefix + item);
}
}
화살표 함수는 함수 자체의 this 바인딩을 갖지 않는다. 따라서 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다. 이를 lexical this라고 함.
ex)
const foo = () => console.log(this);
foo(); // window
const counter = {
num : 1,
increase : () => ++this.num
};
console.log(counter.increase());// NaN
✔️ 화살표함수는 함수자체의 this바인딩을 갖지 않기 때문에 call, apply, bind 메서드를 사용해도 화살표 내부의 this를 교체할 수 없음!
window.x = 1;
const arrow = () => this.x;
console.log(arrow.call({x : 10})); // 1
✔️ 메서드를 화살표 함수로 정의하는 것은 피해야함! (메서드 : ES6 메서드가 아닌 일반적인 의미의 메서드) 👉 this를 참조하지 못하므로
✔️ 화살표 함수는 함수 자체의 super 바인딩을 갖지 않음. 따라서 화살표 함수 내부에서 super를 참조하면 this와 마찬가지로 상위 스코프의 super를 참조한다.
ex)
class Base {
constructor(name){
this.name = name;
}
sayHi(){
return `Hi! ${this.name}`;
}
}
class Derived extends Base {
sayHi = () => `${super.sayHi()} Hi!`;
}
const derived = new Derived('Lee');
console.log(derived.sayHi()); // Lee Hi!
✔️ 화살표 함수는 함수 자체의 arguments 바인딩을 갖지 않음. this와 마찬가지로 상위 스코프의 arguments를 참조.
ex)
(funtion(){
// 화살표 함수 foo의 arguments는 상위 스코프인 즉시 실행 함수의 arguments를 가리킨다.
const foo = () => console.log(arguments); // [Arguments]{ '0' : 1, '1' : 2 }
foo(3,4);
}(1,2));
const foo = (1,2) => console.log(arguments);
foo(1,2); // Reference Error 전역에서는 arguments 객체가 존재하지 않으므로! 함수내부에서만 유효함!
Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다.
✔️ 먼저 선언된 매개변수에 할당된 인수를 제외한 나머지 인수들로 구서된 배열에 할당된다. 따라서 Rest 파라미터는 반드시 마지막 파라미터이어야함.
function foo(param, ...rest){
console.log(param); // 1
console.log(rest); // [2,3,4,5]
}
foo(1,2,3,4,5);
✔️ rest 파라미터를 활용함으로써 유사 배열 객체인 arguments 객체를 배열로 변환하는 번거로움을 피할 수 있음.
ES6에서 도입된 매개변수 기본값
function sum(x=0,y=0){
return x + y;
}
console.log((sum(1)); // 1
참고로, Rest 파라미터에는 기본값 지정할 수 없음!
또한 매개변수 기본값은 함수 정의 시 선언한 매개변수 개수를 나타내는 함수 객체의 length 프로퍼티와 arguments 객체에 아무런 영향 주지 않음!