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
|
---
title: Object-oriented JavaScript for beginners
slug: conflicting/Learn/JavaScript/Objects/Classes_in_JavaScript
tags:
- Article
- Beginner
- CodingScripting
- JavaScript
- Learn
- l10n:priority
translation_of: Learn/JavaScript/Objects/Object-oriented_JS
original_slug: Learn/JavaScript/Objects/Object-oriented_JS
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}</div>
<p class="summary">자, 이제 기초 단계를 벗어나서,객체지향 JavaScript (OOJS) 을 보도록 하죠 — 이 문서에서 객체지향 (OOP) 이론에 대한 기초를 훑어본 후, 자바스크립트가 생성자와 함수를 통해 객체 클래스 개념을 따라했는지, 그리고 어떻게 객체를 만드는지 알아볼겁니다.</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">선수조건:</th>
<td>컴퓨터 기본지식, HTML과 CSS에 대한 기본적인 이해,자바스크립트에 어느 정도 익숙할 것 (see <a href="/ko/docs/Learn/JavaScript/First_steps">First steps</a> and <a href="/ko/docs/Learn/JavaScript/Building_blocks">Building blocks</a>). OOJS 기초 지식 (see <a href="/ko/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>).</td>
</tr>
<tr>
<th scope="row">학습목표:</th>
<td>객체지향에 대한 기본 지식을 습득 하고, 객체 지향이 자바스크립트에 어떻게 적용되었는지 ( "모든 것은 객체다") 와 어떻게 생성자와 객체 인스턴스를 만드는지에 대해 이해한다.</td>
</tr>
</tbody>
</table>
<h2 id="객체지향_프로그래밍_—_기초">객체지향 프로그래밍 — 기초</h2>
<p>객체지향 프로그래밍(OOP)의 개요를 설명하는 것으로 시작하겠습니다. 지금 단계에서 OOP의 모든 것을 설명면 너무 복잡해서 혼란만을 가중시킬 것이기 때문에 최대한 간단히 설명하겠습니다. OOP의 기본 컨셉은 프로그램 내에서 표현하고자 하는 실 세계(real world)의 일들을 객체를 사용해서 모델링 하고, 객체를 사용하지 않으면 불가능 혹은 무지 어려웠을 일들을 쉽게 처리하는 방법을 제공한다는 것입니다.</p>
<p>객체는 당신이 모델링하고자 하고자 하는 일이나 기능 혹은 필요한 행동들을 표현하는 프로그램 코드와 그와 연관된 데이터로 구성됩니다. 객체는 데이터(그리고, 함수 역시)를 감싸서 ,(공식적인 표현으로는 <strong>encapsulate</strong>) 객체 패키지(해당 객체를 참조하기 위한 이름. <strong>namespace </strong>라고도 불리죠)안에 보관합니다. 이는 계층 구조를 만드는데 용이하고 사용하기에도 쉽게 하기 위해서죠; 또한, 객체는 네트워크를 통해 쉽게 전송될 수 있도록 데이터를 저장하는 용도로도 많이 사용됩니다.</p>
<h3 id="객체_템플릿_정의">객체 템플릿 정의</h3>
<p>자, 학교의 선생님과 학생들의 정보를 보여주는 간단한 프로그램이 있다고 칩시다. 여기서는 OOP의 일반적인 개념만을 살펴볼 뿐이지, 특정 언어에 국한된 내용을 이야기하지는 않을겁니다.</p>
<p>시작해보자면, <a href="/ko/docs/Learn/JavaScript/Objects/Basics">first objects article</a> 에서 배웠던 Person 객체로 돌아가봅시다. 거기서 "사람"에 대한 기초적인 데이터와 기능을 정의했었죠. "사람"을 구별할 수 있는 특징은 많습니다 (그들의 주소, 키,신발사이즈, DNA 프로필, 여권번호, 중요한 개인적 자실 등 ...) ,하지만 이 예제에서는 오직 이름, 나이, 성별 그리고 취미만을 다룰겁니다. 여기에 더불어 이 데이터를 기반으로 각 개인에 대한 간단한 소개말과 인사말을 표시할 수 있도록 할 겁니다 . 이런 과정을 <strong>추상화</strong> — 프로그래머의 의도에 맞추어 가장 중요한 것들만을 뽑아서 복잡한 것들을 보다 단순한 모델로 변환하는 작업 - 라고 합니다.<img alt="" src="https://mdn.mozillademos.org/files/13889/person-diagram.png" style="display: block; height: 219px; margin: 0px auto; width: 610px;"></p>
<h3 id="실제_객체_생성">실제 객체 생성</h3>
<p><strong>객체 인스턴스</strong>는 클래스를 통해서 만들 수 있습니다.— 객체는 클래스에 정의된 데이터와 함수를 갖습니다. Person클래스를 통해서, 실제 '사람' 객체를 생성할 수 있습니다.:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/15163/MDN-Graphics-instantiation-2-fixed.png" style="display: block; height: 702px; margin: 0px auto; width: 695px;"></p>
<p>클래스로부터 객체의 인스턴스가 생성될 때는 클래스의 <strong>생성자 함수</strong> 가 호출됩니다.클래스에서 객체 인스턴스가 생성되는 일련의 과정을 인스턴스화(<strong>instantiation</strong>)라고 합니다 — 객체의 인스턴스는 클래스를 통해 만들어집니다.</p>
<h3 id="특별한_클래스">특별한 클래스</h3>
<p>자, 이번에는 일반적인 사람이 아니라 — 일반적인 사람보다 세분화된 선생님과 학생들이 필요합니다. OOP 에서는,특정 클래스를 기반으로 새로운 클래스를 만들 수 있습니다 — <strong>child 클래스</strong> 는 <strong>부모 클래스</strong>를<strong> </strong><strong>상속</strong> 받아서 만들어집니다. child 클래스는 상속을 통해 부모 클래스에 정의된 데이터와 함수를 고스란히 사용할 수 있습니다. 클래스마다 기능이 달라지는 부분이 있다면, 직접 해당 클래스에 원하는 기능을 정의할 수 있습니다.</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/13881/MDN-Graphics-inherited-3.png" style="display: block; height: 743px; margin: 0px auto; width: 700px;"></p>
<p>이것은 매우 유용합니다. 이름,성별,나이 등과 같이 선생님과 학생이 공유하는 많은 공통적인 특징들을 한번만 정의해도 되기 때문이죠. 또한 서로 다른 클래스에 같은 기능을 따로 정의할 수도 있습니다. 정의된 각각의 기능은 서로 다른 namespace에 존재하기 때문입니다. 예를 들어, 학생의 인사는 "안녕, 난 [이름]이야." 와 같은 형식이 될 것입니다. (ex) 안녕, 난 샘이야.) 반면 선생님은 "안녕하세요, 제 이름은 [성] [이름]이고 [과목명]을 담당하고 있습니다." 와 같이 좀 더 격식있는 형식을 사용할 것입니다. (ex) 안녕하세요, 제 이름은 데이브 그리피스이고 화학을 담당하고 있습니다.)</p>
<div class="note">
<p><strong>노트</strong>: 혹시 궁금해 하실까봐 말씀드리면, 여러 객체 타입에 같은 기능을 정의할 수 있는 능력을 멋진 용어로 <strong>"다형성(polymorphism)"</strong> 이라고 합니다.</p>
</div>
<p>이제 자식 클래스들로부터 객체 인스턴스를 만들 수 있습니다. 예를 들면 :</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/13885/MDN-Graphics-instantiation-teacher-3.png" style="display: block; height: 743px; margin: 0px auto; width: 700px;"></p>
<p>다음 부분에선, 어떻게 객체지향 프로그래밍 이론이 자바스크립트에 실제로 적용될 수 있는지 살펴보겠습니다.</p>
<h2 id="생성자와_객체_인스턴스">생성자와 객체 인스턴스</h2>
<p>자바스크립트는 객체와 그 기능을 정의하기 위해 <strong>생성자 함수</strong>라고 불리는 특별한 함수를 사용합니다. 이는 보통 우리가 얼마나 많은 객체들을 생성해야 할지 모르기 때문에 유용합니다. 생성자는 효율적으로 필요한 만큼 객체를 생성하고, 데이터와 함수들을 설정하는 방법을 제공합니다.</p>
<p>생성자로부터 새로운 객체 인스턴스가 생성되면, 객체의 핵심 기능 (프로토타입에 의해 정의됩니다. <a href="/ko/docs/Learn/JavaScript/Objects/Object_prototypes">Object prototypes</a> 글에서 자세히 다룰 것입니다.)이 <strong>프로토타입 체인</strong>에 의해 연결됩니다.</p>
<p>자바스크립트에서 생성자를 이용해 클래스를 만들고, 클래스에서 객체 인스턴스를 만드는 방법을 알아봅시다. 가장 먼저, 첫 객체 글에서 보았던 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs.html">oojs.html</a> 파일을 로컬에 새로 복사하십시오.</p>
<h3 id="간단한_예제">간단한 예제</h3>
<ol>
<li>어떻게 일반적인 함수를 이용해 한 사람을 정의할 수 있는지부터 보겠습니다. 이 함수를 <code>script</code> 태그 안에 추가하세요:
<pre class="brush: js">function createNewPerson(name) {
var obj = {};
obj.name = name;
obj.greeting = function() {
alert('Hi! I\'m ' + this.name + '.');
};
return obj;
}</pre>
</li>
<li>이제 이 함수를 호출하여 새로운 사람을 만들 수 있습니다. 브라우저의 자바스크립트 콘솔을 열어 다음 코드를 입력해보세요:
<pre class="brush: js">var salva = createNewPerson('Salva');
salva.name;
salva.greeting();</pre>
이것은 잘 작동하지만, 썩 깔끔하진 않습니다. 객체를 만들기를 원하는데, 왜 굳이 빈 객체를 만들고 내용을 채워 리턴해야 할까요? 다행스럽게도 자바스크립트는 생성자 함수의 형태로 간단한 단축 명령을 제공합니다. 하나 만들어 보도록 하죠!</li>
<li>이전의 createNewPerson 함수를 다음의 코드로 교체하세요:
<pre class="brush: js">function Person(name) {
this.name = name;
this.greeting = function() {
alert('Hi! I\'m ' + this.name + '.');
};
}</pre>
</li>
</ol>
<p>생성자 함수는 클래스의 자바스크립트 버전입니다. 이 함수가 함수가 가질 것 같은 모든 특징을 가지고 있지만, 아무것도 리턴하지 않고 객체를 만들지도 않는다는 것을 깨달으셨나요? 생성자 함수는 단순히 프로퍼티와 메소드를 정의합니다. 또 이를 정의할 때 this 라는 키워드가 사용되고 있는 것을 보실 수 있습니다. 이것은 객체 인스턴스가 생성될 때마다, 객체의 <code>name</code> 프로퍼티가 생성자 함수 호출에서 전달된 name 값과 같아질 것이라고 말하고 있습니다. 그리고 <code>greeting()</code> 메소드 역시 생성자에서 전달된 name 값을 사용할 것입니다.</p>
<div class="note">
<p><strong>노트</strong>: 관습적으로, 생성자 함수명은 대문자로 시작하게 합니다. 이 규칙은 생성자 함수가 코드 안에서 잘 구별되도록 해줍니다.</p>
</div>
<p>그래서 어떻게 생성자 함수를 호출하여 객체들을 만들까요?</p>
<ol>
<li>이전 코드 아래에 다음 코드들을 추가하세요:
<pre class="brush: js">var person1 = new Person('Bob');
var person2 = new Person('Sarah');</pre>
</li>
<li>
<p>코드를 저장하고 브라우저를 새로고침합니다. 자바스크립트 콘솔에 다음 코드를 입력해보세요:</p>
</li>
<li>
<pre class="brush: js">person1.name
person1.greeting()
person2.name
person2.greeting()</pre>
</li>
</ol>
<p>멋지군요! 이제 두 객체가 페이지에 생성된 것이 보입니다. 각각은 서로 다른 namespace에 저장되어있습니다. 객체의 프로퍼티와 메소드들을 사용하려면, <code>person1</code> 또는 <code>person2</code>로부터 호출하여야 합니다. 두 객체의 기능은 따로 패키징되어 서로 충돌하지 않을 것입니다. 그리고 두 Person 객체는 각각 고유의 <code>name</code> 프로퍼티와 <code>greeting()</code> 메소드를 사용할 수 있습니다. 이 둘이 생성될 때 부여받은 자신의 <code>name</code> 값을 사용한다는 것에 주목하십시오. 이것이 <code>this</code>를 사용하는 매우 중요한 이유 중 하나입니다. 객체들은 다른 값이 아니라, 그들이 가진 고유의 값을 사용합니다.</p>
<p>생성자 호출을 다시 봅시다:</p>
<pre class="brush: js">var person1 = new Person('Bob');
var person2 = new Person('Sarah');</pre>
<p>각각의 경우, <code>new</code> 키워드가 브라우저에게 우리가 새로운 객체 인스턴스를 만들고 싶어한다는 것을 알려줍니다. 괄호로 감싸진 매개변수들과 함께 생성자 이름을 호출하고, 결과는 변수에 담겨집니다. 일반적인 함수가 호출되는 방식과 매우 유사하죠. 각각의 인스턴스는 다음 정의에 따라 생성됩니다.</p>
<pre class="brush: js">function Person(name) {
this.name = name;
this.greeting = function() {
alert('Hi! I\'m ' + this.name + '.');
};
}</pre>
<p>새 객체가 생성된 이후, <code>person1</code>과 <code>person2</code> 변수는 다음 객체들을 가지게 됩니다.</p>
<pre class="brush: js">{
name: 'Bob',
greeting: function() {
alert('Hi! I\'m ' + this.name + '.');
}
}
{
name: 'Sarah',
greeting: function() {
alert('Hi! I\'m ' + this.name + '.');
}
}</pre>
<p>우리가 생성자 함수를 호출할 때마다 매번 <code>greeting()</code> 함수를 다시 정의하는 것이 보입니다. 최선의 방법은 아니죠. 이를 피하기 위해, 우리는 prototype에 함수를 정의합니다. 이를 차후에 다시 살펴보겠습니다.</p>
<h3 id="생성자_완성시키기">생성자 완성시키기</h3>
<p>위에서 살펴본 예제는 시작에 불과합니다. 최종적인 <code>Person()</code> 생성자를 만들어봅시다.</p>
<ol>
<li>여태 작성한 코드를 지우고 아래의 생성자로 대체하세요. 원리는 이전의 예제와 똑같으며, 약간 더 복잡할 뿐입니다:
<pre class="brush: js">function Person(first, last, age, gender, interests) {
this.name = {
'first': first,
'last' : last
};
this.age = age;
this.gender = gender;
this.interests = interests;
this.bio = function() {
alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
};
this.greeting = function() {
alert('Hi! I\'m ' + this.name.first + '.');
};
}</pre>
</li>
<li>이제 생성자로 객체 인스턴스를 만들기 위해, 아래에 이 코드를 추가하세요:
<pre class="brush: js">var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);</pre>
</li>
</ol>
<p>이제 이전에 해보았듯이, 브라우저의 자바스크립트 콘솔에서 프로퍼티와 메소드를 사용할 수 있습니다:</p>
<pre class="brush: js">person1['age']
person1.interests[1]
person1.bio()
// etc.</pre>
<div class="note">
<p><strong>노트</strong>: 만약 실행에 문제가 생긴다면, 저희가 준비한 코드와 비교해보세요. <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-finished.html">oojs-class-finished.html</a> (<a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-finished.html">또한 실제로 실행되는 모습을 보세요</a>).</p>
</div>
<h3 id="추가_예제">추가 예제</h3>
<p>이를 시작하기 위해서, 몇 개의 객체를 더 생성하는 코드를 추가해보세요. 그리고 생성된 객체 인스턴스의 멤버들을 사용하거나 바꿔보세요.</p>
<p>더 나아가, 우리의 <code>bio()</code> 메소드엔 몇 가지 문제점이 있습니다. 먼저 결과가 항상 대명사 "He"를 포함한다는 점입니다. 생성된 사람이 여성이거나 다른 성별 분류를 가질지라도 말이죠. 그리고 <code>interests</code> 배열에 몇 개가 포함되어 있더라도 bio는 2개의 취미만을 출력합니다. 클래스 정의 (생성자)에서 이를 해결할 방법이 있을까요? 자유롭게 생성자를 수정해보세요. (약간의 조건문과 반복문이 필요할지도 모르겠습니다). 어떻게 성별에 따라, 혹은 취미의 개수에 따라 문장이 다르게 구성되어야할지 생각해보세요 .</p>
<div class="note">
<p><strong>노트</strong>: 하다가 막힌다면, 저희가 제공하는 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-further-exercises.html">GitHub 저장소의 모법 답안</a> (<a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html">그리고 실행 버전</a>)을 참고하세요. 하지만 일단 직접 해보시죠!</p>
</div>
<h2 id="객체_인스턴스를_생성하는_다른_방법들">객체 인스턴스를 생성하는 다른 방법들</h2>
<p>여태까지 객체 인스턴스를 만드는 두 가지 방법을 살펴보았습니다. <a href="/ko/docs/Learn/JavaScript/Objects/Basics#Object_basics">객체 리터럴을 선언하는 방법</a>과, 생성자 함수를 사용하는 방법(위를 보세요)이죠.</p>
<p>이것들은 잘 동작하지만, 다른 방법들도 있습니다. 웹에서 정보를 찾다가 마주칠 경우를 대비해 익숙해져보는 것도 좋을 것 같습니다.</p>
<h3 id="Object()_생성자">Object() 생성자</h3>
<p>첫번째로, 새 객체를 만들기 위해 <code><a href="/ko/docs/Web/JavaScript/Reference/Global_Objects/Object">Object()</a></code> 생성자를 사용할 수 있습니다. 네, 최초의 object 역시 생성자를 가지고 있습니다. 빈 객체를 생성하는 함수이죠.</p>
<ol>
<li>브라우저의 자바스크립트 콘솔에 아래 코드를 입력해보세요:
<pre class="brush: js">var person1 = new Object();</pre>
</li>
<li>이는 빈 객체를 <code>person1</code> 변수에 담습니다. 이제 이 객체에 점 표기법이나 괄호 표기법을 이용해 프로퍼티와 메소드들을 추가할 수 있습니다. 이 예제 코드를 콘솔 창에 입력해보세요.
<pre class="brush: js">person1.name = 'Chris';
person1['age'] = 38;
person1.greeting = function() {
alert('Hi! I\'m ' + this.name + '.');
};</pre>
</li>
<li>사전에 프로퍼티와 메소드를 정의하기 위해, <code>Object()</code> 생성자의 파라미터로 객체 리터럴을 전달할 수도 있습니다. 이 예제 코드를 콘솔 창에 입력해보세요.
<pre class="brush: js">var person1 = new Object({
name: 'Chris',
age: 38,
greeting: function() {
alert('Hi! I\'m ' + this.name + '.');
}
});</pre>
</li>
</ol>
<h3 id="create()_함수_사용">create() 함수 사용</h3>
<p>생성자는 여러분의 코드에 규칙을 부여해줍니다. 일단 생성자를 만들어두면, 이를 이용해 원하는대로 인스턴스를 생성할 수 있고, 이 인스턴스가 어디서 유래했는지 명백합니다.</p>
<p>하지만 몇몇 사람들은 객체 인스턴스들을 생성할 때 먼저 생성자를 만들기를 원하지 않습니다. 특히 그들이 적은 수의 객체만을 생성할 때 말이죠. 자바스크립트는 <code><a href="/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/create">create()</a></code>라는 내장함수를 가지고 있어 이를 가능하게 해줍니다. 이를 이용하면, 이미 존재하는 객체를 이용해 새로운 객체를 만들 수 있습니다.</p>
<ol>
<li>이전 섹션에서 완료한 예제를 브라우저에서 열어, 아래 코드를 콘솔창에 입력해보세요.
<pre class="brush: js">var person2 = Object.create(person1);</pre>
</li>
<li>이제 이 코드를 입력해보세요.
<pre class="brush: js">person2.name
person2.greeting()</pre>
</li>
</ol>
<p><code>person2</code>가 <code>person1</code>을 기반으로 만들어졌습니다. 새 객체는 원 객체와 같은 프로퍼티와 메소드들을 가집니다. </p>
<p><code>create()</code> 함수의 한 가지 단점은 익스플로러 8에서는 지원하지 않는다는 점입니다. 따라서 오래된 브라우저들까지 지원하고 싶다면 생성자를 사용하는 것이 효과적입니다.</p>
<p>다음에 <code>create()</code> 함수의 효과에 대해 더 살펴보겠습니다.</p>
<h2 id="요약">요약</h2>
<p>이 글은 객체지향 이론을 요약하여 설명해줍니다. 모든 부분을 다루지는 않지만, 지금 어떤 것들을 다루고 있는지에 대한 아이디어 정도는 얻을 수 있습니다. 게다가 객체 인스턴스를 생성하는 여러가지 방법에 대해서도 알아보기 시작했습니다.</p>
<p>다음 글에서는 자바스크립트 객체 프로토타입에 대해 탐험해보겠습니다.</p>
<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}</p>
<h2 id="In_this_module">In this module</h2>
<ul>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics">Object basics</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS">Object-oriented JavaScript for beginners</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">Object prototypes</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Inheritance">Inheritance in JavaScript</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Working with JSON data</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Object building practice</a></li>
<li><a href="/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">Adding features to our bouncing balls demo</a></li>
</ul>
<p> </p>
|