aboutsummaryrefslogtreecommitdiff
path: root/files/ru/learn/javascript/объекты/object-oriented_js/index.html
blob: 0299268a90ab95f8cb0f56e3845a7bc9a6bc51e7 (plain)
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
---
title: Объектно-ориентированный JavaScript для начинающих
slug: Learn/JavaScript/Объекты/Object-oriented_JS
tags:
  - Constructor
  - Create
  - JavaScript
  - OOJS
  - Object
  - Новичку
  - ООП
  - экземпляр
translation_of: 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) — данная статья дает базовое представление о теории объектно-ориентированного программирования (ООП), далее рассмотрено как JavaScript эмулирует классы объектов с помощью функции-конструктора и как создаются экземпляры объектов.</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">Необходимые знания:</th>
   <td>
    <p class="brush: html">Базовая компьютерная грамотность, базовое понимание HTML и CSS, знакомство с основами JavaScript (см. <a href="/en-US/docs/Learn/JavaScript/First_steps">Первые шаги</a> и <a href="/en-US/docs/Learn/JavaScript/Building_blocks">C</a>труктурные элементы JavaScript) и основы OOJS (см. <a href="/en-US/docs/Learn/JavaScript/Object-oriented/Introduction">Введение в объекты</a>).</p>
   </td>
  </tr>
  <tr>
   <th scope="row">Цель:</th>
   <td>Понять основную теорию объектно-ориентированного программирования, как это относится к JavaScript («все является объектом») и как создавать конструкторы и экземпляры объектов.</td>
  </tr>
 </tbody>
</table>

<h2 id="Объектно-ориентированное_программирование_основы">Объектно-ориентированное программирование: основы</h2>

<p>Начнём с упрощённого высокоуровневого представления о том, что такое <em>объектно-ориентированное программирование</em> <em>(ООП)</em>. Мы говорим упрощённого, потому что ООП может быстро стать очень сложным, и если сейчас дать полный курс, вероятно, можно запутать больше, чем помочь. Основная идея ООП заключается в том, что мы используем объекты для отображения моделей из реального мира в наших программах и/или упрощения доступа к функциям, которые в противном случае было бы трудно или невозможно использовать.</p>

<p>Объекты могут содержать данные и код, представляющие информацию о том, что вы пытаетесь смоделировать, а также о том, какие у этих объектов должны быть функциональные возможности или поведение. Данные объекта (а часто так же и функции) могут быть точно сохранены (официальный термин "<strong>инкапсулированы"</strong>) внутри пакета объекта, упрощая структуру и доступ к ним. Пакету объекта может быть присвоено определенное имя, на которое можно сослаться и которое иногда называют <strong>пространством имен.</strong> Объекты также широко используются в качестве хранилищ данных, которые могут быть легко отправлены по сети.</p>

<h3 id="Определение_шаблона_объекта">Определение шаблона объекта</h3>

<p>Рассмотрим простую программу, которая отображает информацию об учениках и учителях в школе. Здесь мы рассмотрим теорию ООП в целом, а не в контексте какого-либо конкретного языка программирования.</p>

<p>Вернёмся к объекту Person из нашей статьи <a href="/ru/docs/Learn/JavaScript/Объекты/Основы">Основы объектов</a>, который определяет общие сведения и функциональные возможности человека. Есть много вещей, которые вы <em>можете</em> узнать о человеке (его адрес, рост, размер обуви, профиль ДНК, номер паспорта, значимые черты личности ...), но в данном случае нас интересует только имя, возраст, пол и интересы, а также мы хотим иметь возможность написать краткую информацию о нём, основываясь на этих данных, и сделать так, чтобы он поздоровался. Это известно как <strong>абстракция</strong> — создание простой модели более сложной сущности, которая представляет её наиболее важные аспекты таким образом, чтобы с ней было удобно работать для выполнения целей нашей программы.</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/13889/person-diagram.png" style="display: block; height: 219px; margin: 0px auto; width: 610px;"></p>

<p>В некоторых языках ООП, это общее определение типа объекта называется <strong>class</strong> (JavaScript использует другой механизм и терминологию, как вы увидите ниже) — это на самом деле не объект, а шаблон, который определяет, какие характеристики должен иметь объект.</p>

<h3 id="Создание_реальных_объектов">Создание реальных объектов</h3>

<p>Из нашего класса мы можем создать <strong>экземпляры объектов</strong> — объекты, содержащие данные и функциональные возможности, определённые в классе. Из нашего класса Person мы теперь можем создавать модели реальных людей:</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/13883/MDN-Graphics-instantiation-2.png" style="display: block; height: 743px; margin: 0px auto; width: 700px;"></p>

<p>Когда экземпляр объекта создается из класса, для его создания выполняется <strong>функция-конструктор </strong>класса. Этот процесс создания экземпляра объекта из класса называется <strong>создание экземпляра (instantiation)</strong> — из класса <strong>создается</strong> экземпляр объекта.</p>

<h3 id="Специализированные_классы">Специализированные классы</h3>

<p>В нашем случае нам не нужны все люди — нам требуются учителя и ученики, которые являются более конкретными типами людей. В ООП мы можем создавать новые классы на основе других классов — эти новые <strong>дочерние классы</strong> могут быть созданы для <strong>наследования</strong> данных и характеристик <strong>родительского класса</strong>, так чтобы можно было использовать функциональные возможности, общие для всех типов объекта, вместо того чтобы дублировать их. Когда функциональность различается между классами, можно по мере необходимости определять специализированные функции непосредственно на них.</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>Это действительно полезно — преподаватели и студенты имеют много общих характеристик, таких как имя, пол и возраст, и удобно определить их только один раз. Вы можете также задать одну и ту же характеристику отдельно в разных классах, поскольку каждое определение этой характеристики будет находиться в отдельном пространстве имен. Например, приветствие студента может быть в форме "Yo, I'm [firstName]" (например <em>Yo, I'm Sam</em>), в то время как учитель может использовать что-то более формальное, такое как "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (например <em>Hello, My name is Mr Griffiths, and I teach Chemistry</em>).</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>Далее мы рассмотрим, как ООП теорию можно применить на практике в JavaScript.</p>

<h2 id="Конструкторы_и_экземпляры_объектов">Конструкторы и экземпляры объектов</h2>

<p>JavaScript использует специальные функции, называемые функциями конструктора (<strong>constructor functions</strong>) для определения объектов и их свойств. Они полезны, потому что вы часто будете сталкиваться с ситуациями, в которых не известно, сколько объектов вы будете создавать; конструкторы позволяют создать столько объектов, сколько нужно эффективным способом, прикреплением данных и функций для объектов по мере необходимости.</p>

<p>Рассмотрим создание классов через конструкторы и создание экземпляров объектов из них в JavaScript. Прежде всего, мы хотели бы, чтобы вы создали новую локальную копию файла <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 notranslate">function createNewPerson(name) {
  const obj = {};
  obj.name = name;
  obj.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
  return obj;
}</pre>
 </li>
 <li>Теперь вы можете создать нового человека, вызвав эту функцию - попробуйте следующие строки в консоли JavaScript браузера:
  <pre class="brush: js notranslate">const salva = createNewPerson('Salva');
salva.name;
salva.greeting();</pre>
  Это работает достаточно хорошо, но код излишне многословен; если мы знаем, что хотим создать объект, зачем нам явно создавать новый пустой объект и возвращать его? К счастью, JavaScript предоставляет нам удобный способ в виде функций-конструкторов - давайте сделаем это сейчас!</li>
 <li>Замените предыдущую функцию следующей:
  <pre class="brush: js notranslate">function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}</pre>
 </li>
</ol>

<p>Функция-конструктор - это JavaScript версия класса. Вы заметите, что в нем есть все признаки, которые вы ожидаете от функции, хотя он ничего не возвращает и явно не создает объект - он в основном просто определяет свойства и методы. Вы также увидите, что ключевое слово this также используется здесь, - это в основном говорит о том, что всякий раз, когда создается один из этих экземпляров объектов, свойство имени объекта будет равно значению <code>name</code>, переданному вызову конструктора, и метод <code>greeting()</code> будет использовать значение имени, переданное также вызову конструктора.</p>

<div class="note">
<p><strong>Примечание:</strong> Имя функции конструктора обычно начинается с заглавной буквы - это соглашение используется для упрощения распознавания функций конструктора в коде.</p>
</div>

<p>Итак, как мы вызываем конструктор для создания некоторых объектов?</p>

<ol>
 <li>Добавьте следующие строки под предыдущим добавлением кода:
  <pre class="brush: js notranslate">let person1 = new Person('Bob');
let person2 = new Person('Sarah');</pre>
 </li>
 <li>Сохраните код и перезагрузите его в браузере и попробуйте ввести следующие строки в консоль JS:
  <pre class="brush: js notranslate">person1.name
person1.greeting()
person2.name
person2.greeting()</pre>
 </li>
</ol>

<p>Круто! Теперь, как вы видите, у нас есть два новых объекта на странице, каждый из которых хранится в отдельном пространстве имен - при доступе к их свойствам и методам вы должны начинать вызовы с <code>person1</code> или <code>person2</code>; функциональность, содержащаяся внутри, аккуратно упакована, поэтому она не будет конфликтовать с другими функциями. Тем не менее, у них есть одно и то же свойство <code>name</code> и <code>greeting()</code>. Обратите внимание, что они используют свое собственное значение <code>name</code>, которое было присвоено им, когда они были созданы; это одна из причин, почему очень важно использовать <code>this</code>, таким образом они будут использовать свои собственные значения, а не какие-либо другие.</p>

<p>Давайте снова посмотрим на вызовы конструктора:</p>

<pre class="brush: js notranslate">let person1 = new Person('Bob');
let person2 = new Person('Sarah');</pre>

<p>В каждом случае ключевое слово <code>new</code> используется, чтобы сообщить браузеру, что мы хотим создать экземпляр нового объекта, за которым следует имя функции с ее необходимыми параметрами, содержащимися в круглых скобках, и результат сохраняется в переменной - очень похоже на то, как вызывается стандартная функция. Каждый экземпляр создается в соответствии с этим определением:</p>

<pre class="brush: js notranslate">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 notranslate">{
  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> каждый раз, что не является идеальным. Чтобы этого избежать, вместо этого мы можем определить функции на прототипе, о которых мы поговорим позже.</p>

<h3 id="Создавая_наш_готовый_конструктор">Создавая наш готовый конструктор</h3>

<p>Пример, рассмотренный выше, был лишь наглядным примером, чтобы вы поняли суть. Теперь, давайте создадим нашу конечную функцию-конструктор <code>Person()</code>.</p>

<ol>
 <li>Замените весь предыдущий код новой функцией конструктора - это, в принципе, тот же самое что и в наглядном примере, но несколько сложнее:
  <pre class="brush: js notranslate">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 notranslate">let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);</pre>
 </li>
</ol>

<p>Как вы могли заметить, вы можете получить доступ к свойствам и методам, как это было ранее, - попробуйте использовать их в консоли JS:</p>

<pre class="brush: js notranslate">person1['age']
person1.interests[1]
person1.bio()
// etc.</pre>

<div class="note"><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="https://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-finished.html">в прямом эфире</a>).</div>

<h3 id="Дальнейшие_упражнения">Дальнейшие упражнения</h3>

<p>Для начала, попробуйте добавить еще пару собственных строк создания объекта и попробуйте получить и установить элементы полученных экземпляров объектов.</p>

<p>Кроме того, есть несколько проблем с нашим методом <code>bio()</code> - вывод всегда включает местоимение «He» ("Он" в пер. с англ.), даже если ваш человек является женщиной или какой-либо другой предпочтительной гендерной классификацией. И <code>bio</code> будет включать только два интереса, даже если в массиве <code>interests</code> указано больше. Можете ли Вы решить, как исправить это в определении класса (конструкторе)? Вы можете поместить любой код, который вам нравится внутри конструктора (вам, вероятно, понадобятся несколько условий и цикл). Подумайте о том, как предложения должны быть структурированы по-разному в зависимости от пола и в зависимости от того, имеет ли число перечисленных интересов 1, 2 или более 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="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Object_basics">объявление объектного литерала</a> и использование функции конструктора (см. выше).</p>

<p>Это имеет смысл, но есть и другие способы - мы бы хотели ознакомить Вас с ними на случай, если Вы встретите их в своих путешествиях по Сети.</p>

<h3 id="Конструктор_Object">Конструктор Object ()</h3>

<p>Прежде всего, вы можете использовать конструктор <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object()</a></code> для создания нового объекта. Да, даже общие объекты имеют конструктор, который генерирует пустой объект.</p>

<ol>
 <li>Попробуйте ввести это в консоль JavaScript вашего браузера:
  <pre class="brush: js notranslate">let person1 = new Object();</pre>
 </li>
 <li>Это сохраняет ссылку на пустой объект в переменную <code>person1</code>. Затем вы можете добавить свойства и методы к этому объекту с использованием точечной или скобочной нотации по желанию; попробуйте эти примеры в консоли:
  <pre class="brush: js notranslate">person1.name = 'Chris';
person1['age'] = 38;
person1.greeting = function() {
  alert('Hi! I\'m ' + this.name + '.');
};</pre>
 </li>
 <li>Вы также можете передать литерал объекта конструктору <code>Object()</code> в качестве параметра, чтобы заполнить его свойствами / методами. Попробуйте это в консоли JS:
  <pre class="brush: js notranslate">let 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>Однако некоторые люди предпочитают создавать экземпляры объектов без предварительного создания конструкторов, особенно если они создают только несколько экземпляров объекта. JavaScript имеет встроенный метод <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create">create()</a></code>, который позволяет вам это делать. С его помощью вы можете создать новый объект на основе любого существующего объекта.</p>

<ol>
 <li>Закончив упражнение из предыдущего раздела, загруженное в браузер, попробуйте это в консоли JavaScript:
  <pre class="brush: js notranslate">let person2 = Object.create(person1);</pre>
 </li>
 <li>Теперь попробуйте:
  <pre class="brush: js notranslate">person2.name
person2.greeting()</pre>
 </li>
</ol>

<p>Вы увидите, что <code>person2</code> был создан на основе <code>person1</code> - он имеет те же свойства и метод, доступные для него.</p>

<p>Одно ограничение метода <code>create()</code> заключается в том, что IE8 не поддерживает его. Поэтому конструкторы могут быть более эффективными, если вы хотите поддерживать старые браузеры.</p>

<p>Подробнее мы рассмотрим особенности метода <code>create()</code> немного позже.</p>

<h2 id="Сводка">Сводка</h2>

<p>В этой статье представлен упрощенный взгляд на объектно-ориентированную теорию — это еще не вся история, но она дает представление о том, с чем мы имеем дело. Кроме того, мы начали рассматривать различные способы создания экземпляров объектов.</p>

<p>В следующей статье мы рассмотрим прототипы объектов JavaScript.</p>

<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}</p>

<h2 id="В_этом_модуле">В этом модуле</h2>

<ul>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/Основы">Основы объекта</a></li>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/Object-oriented_JS">Объектно-ориентированный JavaScript для начинающих</a></li>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/Object_prototypes">Прототипы объектов</a></li>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/Inheritance">Наследование в JavaScript</a></li>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/JSON">Работа с данными JSON</a></li>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/Object_building_practice">Практика построения объектов</a></li>
 <li><a href="/ru/docs/Learn/JavaScript/Объекты/Adding_bouncing_balls_features">Добавление функций в нашу демонстрацию прыгающих шаров</a></li>
</ul>