1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
|
---
title: Classes
slug: Web/JavaScript/Reference/Classes
translation_of: Web/JavaScript/Reference/Classes
---
<div>{{JsSidebar("Classes")}}</div>
<p>Class는 객체를 생성하기 위한 템플릿입니다. 클래스는 데이터와 이를 조작하는 코드를 하나로 추상화합니다. 자바스크립트에서 클래스는 프로토타입을 이용해서 만들어졌지만 ES5의 클래스 의미와는 다른 문법과 의미를 가집니다.</p>
<h2 id="Class_정의">Class 정의</h2>
<p>Class는 사실 "특별한 {{jsxref("Functions", "함수", "", "true")}}"입니다. 함수를 {{jsxref("Operators/function", "함수 표현식", "", "true")}}과 {{jsxref("Statements/function", "함수 선언", "", "true")}}으로 정의할 수 있듯이 class 문법도 {{jsxref("Operators/class", "class 표현식", "", "true")}} and {{jsxref("Statements/class", "class 선언", "", "true")}} 두 가지 방법을 제공합니다.</p>
<h3 id="Class_선언">Class 선언</h3>
<p>Class를 정의하는 한 가지 방법은 <strong>class 선언</strong>을 이용하는 것입니다. class를 선언하기 위해서는 클래스의 이름(여기서 "Rectangle")과 함께 <code>class</code> 키워드를 사용해야 합니다.</p>
<pre class="brush: js ">class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}</pre>
<h4 id="Hoisting">Hoisting</h4>
<p><strong>함수 선언</strong>과 <strong>클래스 선언</strong>의 중요한 차이점은 함수의 경우 정의하기 하기 전에 호출할 수 있지만, 클래스는 반드시 정의한 뒤에 사용할 수 있다는 점입니다. 다음 코드는 {{jsxref("ReferenceError")}}를 던질 것입니다.</p>
<pre class="brush: js example-bad ">const p = new Rectangle(); // ReferenceError
class Rectangle {}
</pre>
<p>예외가 발생하는 이유는 클래스가 {{Glossary("Hoisting", "호이스팅")}}될 때 초기화는 되지 않기 때문입니다.</p>
<h3 id="Class_표현식">Class 표현식</h3>
<p><strong>Class 표현식</strong>은 class를 정의하는 또 다른 방법입니다. Class 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있습니다. 이름을 가진 class 표현식의 이름은 클래스 body의 local scope에 한해 유효합니다. (하지만, 클래스의 (인스턴스 이름이 아닌) {{jsxref("Function.name", "name")}} 속성을 통해 찾을 수 있습니다).</p>
<pre class="brush: js ">// unnamed
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 출력: "Rectangle"
// named
let Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 출력: "Rectangle2"
</pre>
<div class="note">
<p><strong>참고:</strong> 클래스 <strong>표현식</strong>에는 {{anch ( "Class 선언")}} 섹션에 설명된 것과 동일한 호이스팅 제한이 적용됩니다.</p>
</div>
<h2 id="Class_body_와_메서드_정의">Class body 와 메서드 정의</h2>
<p>Class body는 중괄호 <code>{}</code> 로 묶여 있는 안쪽 부분입니다. 이곳은 여러분이 메서드나 constructor와 같은 class 멤버를 정의할 곳입니다.</p>
<h3 id="Strict_mode">Strict mode</h3>
<p>클래스의 본문(body)은 {{jsxref("Strict_mode", "strict mode", "", "true")}}에서 실행됩니다. 즉, 여기에 적힌 코드는 성능 향상을 위해 더 엄격한 문법이 적용됩니다. 그렇지 않으면, 조용히 오류가 발생할 수 있습니다. 특정 키워드는 미래의 ECMAScript 버전용으로 예약됩니다.</p>
<h3 id="Constructor_생성자">Constructor (생성자)</h3>
<p>{{jsxref("Classes/constructor", "constructor", "", "true")}} 메서드는 <code>class</code> 로 생성된 객체를 생성하고 초기화하기 위한 특수한 메서드입니다. "constructor" 라는 이름을 가진 특수한 메서드는 클래스 안에 한 개만 존재할 수 있습니다. 만약 클래스에 여러 개의 <code>constructor</code> 메서드가 존재하면 {{jsxref("SyntaxError")}} 가 발생할 것입니다.</p>
<p>constructor는 부모 클래스의 constructor를 호출하기 위해 <code>super</code> 키워드를 사용할 수 있습니다.</p>
<h3 id="프로토타입_메서드">프로토타입 메서드</h3>
<p>{{jsxref("Functions/Method_definitions", "메서드 정의", "", "true")}}도 참조해보세요.</p>
<pre class="brush: js ">class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// 메서드
calcArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100</pre>
<h3 id="정적_메서드와_속성">정적 메서드와 속성</h3>
<p>{{jsxref("Classes/static", "static", "", "true")}} 키워드는 클래스를 위한 정적(static) 메서드를 정의합니다. 정적 메서드는 클래스의 인스턴스화(<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#The_object_(class_instance)" title='An example of class instance is "var john = new Person();"'>instantiating</a>) 없이 호출되며, 클래스의 인스턴스에서는 호출할 수 없습니다. 정적 메서드는 어플리케이션(application)을 위한 유틸리티(utility) 함수를 생성하는 데 주로 사용됩니다. 반면, 정적 속성은 캐시, 고정 환경설정 또는 인스턴스 간에 복제할 필요가 없는 기타 데이터에 유용합니다.</p>
<pre class="brush: js ">class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755</pre>
<h3 id="프로토타입_및_정적_메서드를_사용한_this_바인딩">프로토타입 및 정적 메서드를 사용한 <code>this</code> 바인딩</h3>
<p>메서드를 변수에 할당 한 다음 호출하는 것과 같이, 정적 메서드나 프로토타입 메서드가 {{jsxref("Operators/this", "this")}} 값 없이 호출될 때, <code>this</code> 값은 메서드 안에서 <code>undefined</code>가 됩니다. 이 동작은 {{jsxref("Strict_mode", "\"use strict\"")}} 명령어 없이도 같은 방식으로 동작하는데, <code>class</code> 문법 안에 있는 코드는 항상 strict mode 로 실행되기 때문입니다.</p>
<pre class="brush: js ">class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
obj.speak(); // the Animal object
let speak = obj.speak;
speak(); // undefined
Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined</pre>
<p>위에 작성된 코드를 전통적 방식의 함수기반의 non–strict mode 구문으로 재작성하면, <code>this</code> 메서드 호출은 기본적으로 {{Glossary("Global_object", "전역 객체")}}인 초기 <code>this</code> 값에 자동으로 바인딩 됩니다. Strict mode에서는 자동 바인딩이 발생하지 않습니다; <code>this</code> 값은 전달된 대로 유지됩니다.</p>
<pre class="brush: js ">function Animal() { }
Animal.prototype.speak = function() {
return this;
}
Animal.eat = function() {
return this;
}
let obj = new Animal();
let speak = obj.speak;
speak(); // global object (in non–strict mode)
let eat = Animal.eat;
eat(); // global object (in non-strict mode)</pre>
<h3 id="인스턴스_속성">인스턴스 속성</h3>
<p>인스턴스 속성은 반드시 클래스 메서드 내에 정의되어야 합니다:</p>
<pre class="brush: js ">class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}</pre>
<p>정적 (클래스사이드) 속성과 프로토타입 데이터 속성은 반드시 클래스 선언부 바깥쪽에서 정의되어야 합니다. </p>
<pre class="brush: js ">Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;
</pre>
<h3 id="Field_선언">Field 선언</h3>
<div class="blockIndicator warning">
<p>public과 private 필드 선언은 자바스크립트 표준화 위원회에 <a href="https://github.com/tc39/proposal-class-fields">실험적 기능 (stage 3)</a> <a href="https://tc39.es/">TC39</a> 로 제안되어있습니다. 현재 이를 지원하는 브라우져는 제한적인 상태입니다만, <a href="https://babeljs.io/">Babel</a> 과 같은 build 시스템을 사용한다면 이 기능을 사용해볼 수 있습니다.</p>
</div>
<h4 id="Public_필드_선언">Public 필드 선언</h4>
<p>자바스크립트의 필드 선언 문법을 사용해서 위의 예제는 아래와 같이 다시 쓰여질 수 있습니다.</p>
<pre class="brush: js ">class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}</pre>
<p>필드를 먼저 선언함으로서 클래스 정의는 self-documenting에 가까워졌고 필드는 언제나 존재하는 상태가 됩니다.</p>
<p>위의 예에서 봤듯이 필드 선언은 기본 값과 같이 선언될 수도 있습니다.</p>
<p>자세한 내용은 {{jsxref("Classes/Public_class_fields", "public class fields", "", "true")}}를 참조하세요.</p>
<h4 id="Private_필드_선언">Private 필드 선언</h4>
<p>private 필드를 사용하면 아래와 같이 예제를 개선할 수 있습니다.</p>
<pre class="brush: js ">class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}</pre>
<p>클래스의 바깥에서 private 필드를 접근하려고 하면 에러가 발생합니다. private필드는 클래스 내부에서만 읽고 쓰기가 가능합니다. 클래스 외부에서 보이지 않도록 정의하였으므로 클래스가 버젼업 되면서 내부 구현이 바뀌더라도 클래스 사용자 입장에서는 이에 아무런 영항을 받지 않도록 할 수 있습니다.</p>
<div class="blockIndicator note">
<p>Private 필드는 사용전에 선언되어야 합니다.</p>
</div>
<p>일반적인 프로퍼티와는 다르게 private 필드는 값을 할당하면서 만들어질 수 없습니다.</p>
<p>자세한 내용은 {{jsxref("Classes/Private_class_fields", "private class fields", "", "true")}}를 참조하세요.</p>
<h2 id="extends를_통한_클래스_상속sub_classing"><code>extends</code>를 통한 클래스 상속(sub classing)</h2>
<p>{{jsxref("Classes/extends", "extends")}} 키워드는 <em>클래스 선언</em>이나 <em>클래스 표현식</em>에서 다른 클래스의 자식 클래스를 생성하기 위해 사용됩니다.</p>
<pre class="brush: js ">class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // super class 생성자를 호출하여 name 매개변수 전달
}
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.</pre>
<p>subclass에 constructor가 있다면, "this"를 사용하기 전에 가장 먼저 super()를 호출해야 합니다.</p>
<p>또한 es5에서 사용되던 전통적인 함수 기반의 클래스를 통하여 확장할 수도 있습니다.</p>
<pre class="brush: js ">function Animal (name) {
this.name = name;
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`);
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
// 유사한 메서드의 경우, 자식의 메서드가 부모의 메서드보다 우선합니다</pre>
<p>클래스는 생성자가 없는 객체(non-constructible)을 확장할 수 없습니다. 만약 기존의 생성자가 없는 객체을 확장하고 싶다면, 이 메서드를 사용하세요. {{jsxref("Object.setPrototypeOf()")}}:</p>
<pre class="brush: js ">const Animal = {
speak() {
console.log(`${this.name} makes a noise.`);
}
};
class Dog {
constructor(name) {
this.name = name;
}
}
// 이 작업을 수행하지 않으면 speak를 호출할 때 TypeError가 발생합니다
Object.setPrototypeOf(Dog.prototype, Animal);
let d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.</pre>
<h2 id="Species">Species</h2>
<p>배열을 상속 받은 MyArray 클래스에서 {{jsxref("Array")}} Object를 반환하고 싶을 수도 있을 것입니다. 그럴때 Species 패턴은 기본 생성자를 덮어쓰도록 해줍니다.</p>
<p>예를 들어, {{jsxref("Array.map", "map()")}}과 같은 기본 생성자를 반환하는 메서드를 사용할 때 이 메서드가 MyArray 객체 대신 <code>Array</code> 객체가 반환하도록 하고 싶을 것입니다. {{jsxref("Symbol.species")}} 심볼은 이러한 것을 가능하게 해줍니다:</p>
<pre class="brush: js ">class MyArray extends Array {
// 부모 Array 생성자로 species 덮어쓰기
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true</pre>
<h2 id="super_를_통한_상위_클래스_호출"><code>super</code> 를 통한 상위 클래스 호출</h2>
<p>{{jsxref("Operators/super", "super")}} 키워드는 객체의 부모가 가지고 있는 메서드를 호출하기 위해 사용됩니다. 이는 프로토타입 기반 상속보다 좋은 점 중 하나입니다.</p>
<pre class="brush: js ">class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(`${this.name} roars.`);
}
}
let l = new Lion('Fuzzy');
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.</pre>
<h2 id="Mix-ins">Mix-ins</h2>
<p>추상 서브 클래스 또는 믹스-인은 클래스를 위한 템플릿입니다. ECMAScript 클래스는 하나의 단일 슈퍼클래스만을 가질 수 있으며, 예를 들어 툴링 클래스로부터의 다중 상속은 불가능합니다. 기능은 반드시 슈퍼클래스에서 제공되어야 합니다.</p>
<p>슈퍼클래스를 인자로 받고 이 슈퍼클래스를 확장하는 서브클래스를 생성하여 반환하는 함수를 사용하여 ECMAScript에서 믹스-인을 구현할 수 있습니다:</p>
<pre class="brush: js ">var calculatorMixin = Base => class extends Base {
calc() { }
};
var randomizerMixin = Base => class extends Base {
randomize() { }
};</pre>
<p>위 믹스-인을 사용하는 클래스는 다음과 같이 작성할 수 있습니다:</p>
<pre class="brush: js ">class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }</pre>
<h2 id="클래스_재정의">클래스 재정의</h2>
<p>클래스는 재정의될 수 없습니다. 재정의를 시도하면 <code>SyntaxError</code> 가 발생합니다.</p>
<p>이를 실험해보고 싶으면 FireFox Web Console (<strong>Tools </strong>><strong> Web Developer </strong>><strong> Web Console</strong>) 에서 같은 이름으로 클래스를 두번 정의하려고 해보세요. 다음과 같은 오류를 보게 될 겁니다. <code>SyntaxError: redeclaration of let <em>ClassName</em>;</code>. (See further discussion of this issue in {{Bug(1428672)}}.) Doing something similar in Chrome Developer Tools gives you a message like <code>Uncaught SyntaxError: Identifier '<em>ClassName</em>' has already been declared at <anonymous>:1:1</code>.</p>
<h2 id="명세서">명세서</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">명세서</th>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td>
</tr>
</tbody>
</table>
<h2 id="브라우저_호환성">브라우저 호환성</h2>
<p>{{Compat("javascript.classes")}}</p>
<h2 id="참조">참조</h2>
<ul>
<li>{{jsxref("Functions", "함수", "", "true")}}</li>
<li>{{jsxref("Statements/class", "class 선언", "", "true")}}</li>
<li>{{jsxref("Operators/class", "class 식", "", "true")}}</li>
<li>{{jsxref("Classes/Public_class_fields", "Public class fields", "", "true")}}</li>
<li>{{jsxref("Classes/Private_class_fields", "Private class fields", "", "true")}}</li>
<li>{{jsxref("Operators/super", "super")}}</li>
<li><a href="https://hacks.mozilla.org/2015/07/es6-in-depth-classes/">블로그 게시물: "ES6 In Depth: Classes"</a></li>
<li><a href="https://github.com/tc39/proposal-class-fields">Fields and public/private class properties proposal (stage 3)</a></li>
</ul>
|