|
1 | 1 | # Chapter 26 - ES6 함수의 추가 기능 |
| 2 | +## 1. 함수의 구분 |
| 3 | +ES6 이전까지 자바스크립트의 함수는 별다른 구분 없이 다양한 목적으로 사용되었다.<br/> |
| 4 | +이는 편리한 것 같지만 실수를 유발시킬 수 있으며 성능 면에서도 손해다. 예제를 살펴 보자. |
| 5 | +```javascript |
| 6 | +var foo = function() { |
| 7 | + return 1; |
| 8 | +}; |
| 9 | + |
| 10 | +// 일반적인 함수로 호출 |
| 11 | +foo(); // -> 1 |
| 12 | + |
| 13 | +// 생성자 함수로 호출 |
| 14 | +new foo(); // -> foo {} |
| 15 | + |
| 16 | +// 메서드로 호출 |
| 17 | +var obj = { foo: foo }; |
| 18 | +obj.foo(); // -> 1 |
| 19 | +``` |
| 20 | + |
| 21 | +ES6 이전의 모든 함수는 사용 목적에 따라 명확한 구분이 없으므로<br/> |
| 22 | +호출 방식에 특별한 제약이 없고 생성자 함수로 호출되지 않아도 프로토타입 객체를 생성한다.<br/> |
| 23 | +자바스크립트 함수의 이러한 특징은 이해하기 혼란스럽고, 프로그램 성능에도 좋지 않다. |
| 24 | + |
| 25 | +이러한 문제를 해결하기 위해, **ES6에서는 함수를 사용 목적에 따라 세 가지 종류로 구분**해 정의했다. |
| 26 | + |
| 27 | +|**ES6 함수의 구분**|`constructor`|`prototype`|`super`|`arguments`| |
| 28 | +|-|-|-|-|-| |
| 29 | +|**일반 함수(Normal)**|O|O|X|O| |
| 30 | +|**메서드(Method)**|X|X|O|O| |
| 31 | +|**화살표 함수(Arrow)**|X|X|X|X| |
| 32 | + |
| 33 | +일반 함수는 constructor이지만, ES6의 메서드와 화살표 함수는 non-constructor이다. 다음 절에서 더 자세히 살펴 보자. |
| 34 | + |
| 35 | +## 2. ES6 Methods: 메서드 |
| 36 | +ES6 사양에서는 메서드에 대한 정의가 명확하게 규정되었다: **메서드 축약 표현으로 정의된 함수만이 '메서드'다.** |
| 37 | +```javascript |
| 38 | +const obj = { |
| 39 | + x: 1, |
| 40 | + foo() { return this.x; }, // ✅ foo는 '메서드'다. |
| 41 | + bar: function() { return this.x; } // ❌ bar는 '메서드'가 아닌 일반 함수다. |
| 42 | +}; |
| 43 | +``` |
| 44 | + |
| 45 | +ES6에서 정의한 '**메서드**'는, 인스턴스를 생성할 수 없는 non-constructor이다. 따라서 생성자 함수로서 호출할 수 없게 된다.<br/> |
| 46 | +ES6 메서드는 인스턴스를 생성할 수 없으므로 prototype 프로퍼티가 없고 프로토타입도 생성하지 않는다.<br/> |
| 47 | +또한, ES6 메서드는 자신을 바인딩한 객체를 가리키는 슬롯 `[[HomeObject]]`를 갖기 때문에, `super` 키워드를 사용할 수 있다. |
| 48 | + |
| 49 | +이처럼 ES6 메서드는 본연의 기능인 `super`를 추가하고, 의미적으로 맞지 않는 기능인 constructor는 제거했다. |
| 50 | + |
| 51 | +## 3. Arrow Functions: 화살표 함수 |
| 52 | +화살표 함수는, 기존의 함수와 비교해 표현만 간략한 게 아니라 **내부 동작도 간략**하다.<br/> |
| 53 | +특히, 화살표 함수는 콜백 함수 내부에서 **`this`가 전역 객체를 가리키는 문제를 해결**한다. |
| 54 | + |
| 55 | +### 3-1. 화살표 함수 정의 |
| 56 | +생략. 책을 참고합시다~ |
| 57 | + |
| 58 | +### 3-2. 화살표 함수와 일반 함수의 차이 |
| 59 | +1. 화살표 함수는 인스턴스를 생성할 수 없는 **non-constructor**이다. |
| 60 | +2. 화살표 함수는 중복된 매개변수 이름을 선언할 수 없다. |
| 61 | +3. 화살표 함수는 함수 자체의 `this`, `arguments`, `super`, `new.target` 바인딩을 **갖지 않는다**. |
| 62 | + |
| 63 | +### 3-3. `this` |
| 64 | +화살표 함수의 `this`는 일반 함수의 그것과 다르게 동작한다. 이건 의도된 설계다:<br/> |
| 65 | +**콜백 함수 내부의 `this`가, 외부 함수의 `this`와 달라 혼란을 발생시키는 문제를 해결하기 위함**이다. |
| 66 | + |
| 67 | +22장에서 배웠듯이 `this`의 값은 함수가 어떻게 호출됐는지에 따라 다르게 바인딩된다.<br/> |
| 68 | +자바스크립트의 이러한 특징은, **콜백 함수를 일반 함수 형태로 호출할 때 골치 아파진다**.<br/> |
| 69 | +일반 함수로 호출하는 모든 함수의 내부에는 `this`가 전역 객체를 가리키지만,<br/> |
| 70 | +그 함수가 만약 클래스 내부라면 `undefined`를 바인딩하게 된다.<br/> |
| 71 | +클래스 내부의 모든 코드에는 strict mode가 적용되는 영향을 받기 때문이다. |
| 72 | +```javascript |
| 73 | +// ❌ Wrong Practice!! ❌ |
| 74 | +class Prefixer { |
| 75 | + constructor(prefix) { |
| 76 | + this.prefix = prefix; |
| 77 | + } |
| 78 | + |
| 79 | + add(arr) { |
| 80 | + return arr.map(function (item) { |
| 81 | + return this.prefix + item; // TypeError: Cannot read property 'prefix' of undefined |
| 82 | + }); |
| 83 | + } |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +이런 문제를 우리는 **콜백 함수 내부의 `this` 문제**라고 부른다.<br/> |
| 88 | +ES6에서는, 콜백 함수 내부의 `this` 문제를 해결하기 위해 화살표 함수를 제안한다. |
| 89 | + |
| 90 | +```javascript |
| 91 | +// ✅ 위 예제를 화살표 함수로 해결 ✅ |
| 92 | +class Prefixer { |
| 93 | + constructor(prefix) { |
| 94 | + this.prefix = prefix; |
| 95 | + } |
| 96 | + |
| 97 | + add(arr) { |
| 98 | + return arr.map(item => this.prefix + item); |
| 99 | + } |
| 100 | +} |
| 101 | +``` |
| 102 | + |
| 103 | +화살표 함수는 함수 자체의 `this` 바인딩을 갖지 않는다.<br/> |
| 104 | +따라서 **화살표 함수 내부에서 `this`를 참조하면 상위 스코프의 `this`를 그대로 참조**한다. |
| 105 | + |
| 106 | +우리는 이를 **Lexical This**라고 부른다.<br/> |
| 107 | +Lexical this는 마치 렉시컬 스코프처럼, 화살표 함수의 `this`는 **함수가 정의된 위치에 의해 결정**되는 `this`임을 의미한다. |
| 108 | + |
| 109 | +한 가지 유의할 점은 객체 내 메서드를 정의할 땐 화살표 함수를 사용하지 않아야 한다는 점이다. |
| 110 | +```javascript |
| 111 | +// ❌ Wrong Practice!! ❌ |
| 112 | +const person = { |
| 113 | + name: 'Lee', |
| 114 | + sayHi: () => console.log(`hey ${this.name}`) |
| 115 | +}; |
| 116 | +``` |
| 117 | +위 예제가 틀린 이유는, sayHi 프로퍼티에 할당한 화살표 함수의 `this`는,<br/> |
| 118 | +메서드를 호출한 객체인 person을 가리키는 게 아니라, 상위 스코프인 전역 객체를 가리키기 때문이다. |
| 119 | + |
| 120 | +따라서 **객체의 메서드를 정의할 땐** 아래처럼 ES6 메서드 축약 표현으로 정의한 **ES6 메서드**를 사용하는 것이 좋다. |
| 121 | +```javascript |
| 122 | +// ✅ 위 예제를 해결 ✅ |
| 123 | +const person = { |
| 124 | + name: 'Lee', |
| 125 | + sayHi() { |
| 126 | + console.log(`hey ${this.name}`); |
| 127 | + } |
| 128 | +}; |
| 129 | +``` |
| 130 | + |
| 131 | +### 3-4. `super` |
| 132 | +화살표 함수는 함수 자체의 super 바인딩을 갖지 않는다.<br/> |
| 133 | +따라서 화살표 함수 안에서 `super`를 참조하면, `this`가 동작하는 것과 마찬가지로 상위 스코프의 `super`를 참조한다. |
| 134 | +```javascript |
| 135 | +class Base { |
| 136 | + constructor(name) { |
| 137 | + this.name = name; |
| 138 | + } |
| 139 | + |
| 140 | + sayHi() { |
| 141 | + return `Hi! ${this.name}`; |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +class Derived extends Base { |
| 146 | + sayHi = () => `${super.sayHi()} how are you doing?`; |
| 147 | +} |
| 148 | + |
| 149 | +const derived = new Derived('Lee'); |
| 150 | +console.log(derived.sayHi()); // Hi! Lee how are you doing? |
| 151 | +``` |
| 152 | + |
| 153 | +### 3-5. `arguments` |
| 154 | +마찬가지로 화살표 함수는 함수 자체의 arguments 바인딩을 갖지 않는다.<br/> |
| 155 | +따라서 화살표 함수 안에서 `arguments`를 참조하면, `this`가 동작하는 것과 마찬가지로 상위 스코프의 `arguments`를 참조한다. |
| 156 | + |
| 157 | +이처럼 **화살표 함수에서 가변 인자 함수**를 구현해야 할 때는<br/> |
| 158 | +(`arguments`를 쓰지 못하므로) 반드시 **Rest 파라미터**를 사용해야 한다. |
| 159 | + |
| 160 | +## 4. Rest 파라미터 |
| 161 | +### 4-1. 기본 문법 |
| 162 | +Rest 파라미터는 아래처럼 함수에 전달된 인수들의 목록을 **배열**로 받는다.<br/> |
| 163 | +Rest 파라미터는 단 하나만 선언할 수 있으며, 다른 일반 매개변수와 섞어 쓸 수 있다. |
| 164 | +```javascript |
| 165 | +function foo(param, ...rest) { |
| 166 | + console.log(param); // 1 |
| 167 | + console.log(rest); // [2, 3, 4, 5] |
| 168 | +} |
| 169 | + |
| 170 | +foo(1, 2, 3, 4, 5); |
| 171 | +``` |
| 172 | + |
| 173 | +### 4-2. Rest 파라미터와 `arguments` 객체 |
| 174 | +Rest 파라미터와 `arguments` 객체 간 가장 큰 차이점은 바로 배열이냐 아니냐에 있다.<br/> |
| 175 | +`arguments` 객체는 유사 배열 객체이므로 반드시 배열로 변환해야만 배열 메서드를 사용할 수 있는 반면,<br/> |
| 176 | +**Rest 파라미터는 배열**이므로 `map`이나 `filter` 같은 배열 메서드를 곧바로 사용할 수 있다. |
| 177 | + |
| 178 | +## 5. 매개변수 기본값 |
| 179 | +생략. 책을 참고합시다~! |
| 180 | + |
| 181 | +끝. |
0 commit comments