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
|
---
title: Практика построения объектов
slug: Learn/JavaScript/Objects/Object_building_practice
tags:
- Guide
- JavaScript
translation_of: Learn/JavaScript/Objects/Object_building_practice
original_slug: Learn/JavaScript/Объекты/Object_building_practice
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}</div>
<p class="summary">В предыдущих статьях мы рассмотрели всю существенную теорию объектов JavaScript и детали синтаксиса, давая вам прочную основу для начала. В этой статье мы погружаемся в практическое упражнение, давая вам больше практики в создании пользовательских объектов JavaScript, с весёлым и красочным результатом.</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">Необходимые знания:</th>
<td>Базовая компьютерная грамотность, базовые знания HTML и CSS, знакомство с основами JavaScript (see <a href="/en-US/docs/Learn/JavaScript/First_steps">First steps</a> and <a href="/en-US/docs/Learn/JavaScript/Building_blocks">Building blocks</a>) и основами OOJS (см. <a href="/en-US/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>В этой статье мы напишем классическую демонстрацию «прыгающих шаров», чтобы показать вам, насколько полезными могут быть объекты в JavaScript. Наши маленькие шары будут подпрыгивать на экране и менять цвет, когда они касаются друг друга. Готовый пример будет выглядеть примерно так:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/13865/bouncing-balls.png" style="display: block; height: 614px; margin: 0px auto; width: 800px;"></p>
<ol>
</ol>
<p>В этом примере будет использоваться <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Canvas API</a> для рисования шаров на экране и API <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame</a> для анимации всего экрана - вам не нужно иметь никаких предыдущих знаний об этих API, и мы надеемся, что к тому моменту, когда вы закончите эту статью, вам будет интересно изучить их больше. По пути мы воспользуемся некоторыми изящными объектами и покажем вам пару хороших приёмов, таких как отскоки шаров от стен и проверка того, попали ли они друг в друга (иначе известный как <strong>обнаружение столкновения</strong>).</p>
<h2 id="Начало_работы">Начало работы</h2>
<p>Для начала создайте локальные копии наших файлов<code><a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/index.html">index.html</a></code>, <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/style.css">style.css</a></code> и <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/main.js">main.js</a></code>. Они содержат следующее:</p>
<ol>
<li>Очень простой HTML-документ, содержащий элемент {{HTMLElement("h1")}}, элемент {{HTMLElement("canvas")}} для рисования наших шаров и элементы для применения нашего CSS и JavaScript в нашем HTML.</li>
<li>Некоторые очень простые стили, которые в основном служат для стилизации и позиционирования <code><h1></code>, и избавляются от любых полос прокрутки или отступы по краю страницы (так что это выглядит красиво и аккуратно).</li>
<li>Некоторые JavaScript, которые служат для настройки элемента <code><canvas></code> и предоставляют общую функцию, которую мы собираемся использовать.</li>
</ol>
<p>Первая часть скрипта выглядит так:</p>
<pre class="brush: js notranslate">var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;</pre>
<p>Этот скрипт получает ссылку на элемент <code><canvas></code>, а затем вызывает метод <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext">getContext()</a></code>, чтобы дать нам контекст, по которому мы можем начать рисовать. Результирующая переменная (ctx) - это объект, который непосредственно представляет область рисования холста и позволяет рисовать на ней 2D-фигуры.</p>
<p>Затем мы устанавливаем переменные, называемые <code>width</code> и <code>height</code>, а также ширину и высоту элемента canvas (представленные свойствами <code>canvas.width</code> и <code>canvas.height</code>), чтобы равняться ширине и высоте окна просмотра браузера (область, на которой отображается веб-страница - это можно получить из свойств {{domxref("Window.innerWidth")}} и {{domxref("Window.innerHeight")}}).</p>
<p>Вы увидите здесь, что мы объединяем несколько назначений вместе, чтобы все переменные были установлены быстрее - это совершенно нормально.</p>
<p>Последний бит исходного скрипта выглядит следующим образом:</p>
<pre class="brush: js notranslate">function random(min, max) {
var num = Math.floor(Math.random() * (max - min + 1)) + min;
return num;
}</pre>
<p>Эта функция принимает два числа в качестве аргументов и возвращает случайное число в диапазоне между ними.</p>
<h2 id="Моделирование_мяча_в_нашей_программе">Моделирование мяча в нашей программе</h2>
<p>В нашей программе будет много шаров, подпрыгивающих вокруг экрана. Поскольку эти шары будут вести себя одинаково, имеет смысл представлять их в виде объекта. Начнём с добавления следующего конструктора в конец нашего кода.</p>
<pre class="brush: js notranslate">function Ball(x, y, velX, velY, color, size) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.color = color;
this.size = size;
}</pre>
<p>Здесь мы включаем некоторые параметры, которые определяют свойства, которым должен соответствовать каждый шар в нашей программе:</p>
<ul>
<li><code>x</code> и <code>y</code> координаты - горизонтальные и вертикальные координаты, где мяч будет запускаться на экране. Координаты могут находиться в диапазоне от 0 (верхний левый угол) до ширины и высоты окна просмотра браузера (нижний правый угол).</li>
<li>горизонтальная и вертикальная скорость (<code>velX</code> и <code>velY</code>) - каждому шару задана горизонтальная и вертикальная скорость; в реальном выражении эти значения будут регулярно добавляться к значениям координат <code>x</code>/<code>y</code>, когда мы начнём анимировать шары, чтобы их перемещать на каждом кадре.</li>
<li><code>color</code> - каждый мяч получает цвет.</li>
<li><code>size</code> - каждый мяч получает размер - это будет его радиус в пикселях.</li>
</ul>
<p>Этим мы сортируем свойства, но что насчёт методов? Мы хотим заставить эти шары на самом деле сделать что-то в нашей программе.</p>
<h3 id="Рисование_шара">Рисование шара</h3>
<p>Сначала добавьте следующий метод <code>draw()</code> к <code>Ball()</code>'s <code>prototype</code>:</p>
<pre class="brush: js notranslate">Ball.prototype.draw = function() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
}</pre>
<p>Используя эту функцию, мы можем сказать нашему шару нарисовать себя на экране, вызвав ряд членов контекста двумерного холста, который мы определили ранее (<code>ctx</code>). Контекст похож на бумагу, и теперь мы хотим, чтобы наше перо рисовало что-то на нем:</p>
<ul>
<li>Во-первых, мы используем <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath">beginPath()</a></code>, чтобы указать, что мы хотим нарисовать фигуру на бумаге.</li>
<li>Затем мы используем <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle">fillStyle</a></code> для определения того, какой цвет нам нужен, - мы устанавливаем его в свойство цвета нашего шара.</li>
<li>Затем мы используем метод <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc">arc()</a></code> для отслеживания формы дуги на бумаге. Его параметры:
<ul>
<li>Положение <code>x</code> и <code>y</code> центра дуги - мы указываем свойства <code>x</code> и <code>y</code> нашего шара.</li>
<li>Радиус нашей дуги - мы указываем свойство <code>size</code> шарика.</li>
<li>Последние два параметра определяют начальное и конечное число градусов по кругу, по которому проходит дуга. Здесь мы указываем 0 градусов и <code>2 * PI</code>, что эквивалентно 360 градусам в радианах (досадно, вы должны указать это в радианах). Это даёт нам полный круг. Если вы указали только <code>1 * PI</code>, вы получите полукруг (180 градусов).</li>
</ul>
</li>
<li>В последнем случае мы используем метод <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fill">fill()</a></code>, который в основном утверждает: «Закончите рисование пути, начатого с <code>beginPath()</code>, и заполните область, которую он занимает с цветом, указанным ранее в <code>fillStyle</code>».</li>
</ul>
<p>Теперь вы можете начать тестирование своего объекта..</p>
<ol>
<li>Сохраните код и загрузите HTML-файл в браузер.</li>
<li>Откройте консоль JavaScript браузера, а затем обновите страницу, чтобы размер холста изменился в соответствии с новой шириной и высотой окна просмотра браузера после открытия консоли.</li>
<li>Чтобы создать новый экземпляр шара, введите следующее:
<pre class="brush: js notranslate">var testBall = new Ball(50, 100, 4, 4, 'blue', 10);</pre>
</li>
<li>Попробуйте вызвать его свойства и методы:
<pre class="brush: js notranslate">testBall.x
testBall.size
testBall.color
testBall.draw()</pre>
</li>
<li>После введения последней строки, вы должны увидеть, как мяч нарисовался где-то на вашем холсте.</li>
</ol>
<h3 id="Обновление_данных_мяча">Обновление данных мяча</h3>
<p>Мы можем нарисовать мяч в нужном положении, но чтобы начать движение мяча, нам нужна функция обновления. Добавьте следующий код внизу вашего файла JavaScript, чтобы добавить метод <code>update()</code> к <code>Ball()</code>'s <code>prototype</code>:</p>
<pre class="brush: js notranslate">Ball.prototype.update = function() {
if ((this.x + this.size) >= width) {
this.velX = -(this.velX);
}
if ((this.x - this.size) <= 0) {
this.velX = -(this.velX);
}
if ((this.y + this.size) >= height) {
this.velY = -(this.velY);
}
if ((this.y - this.size) <= 0) {
this.velY = -(this.velY);
}
this.x += this.velX;
this.y += this.velY;
}</pre>
<p>Первые четыре части функции проверяют, достиг ли шар края холста. Если это так, мы изменяем полярность соответствующей скорости, чтобы заставить шар двигаться в противоположном направлении. Так, например, если мяч двигался вверх (положительный <code>velY</code>), то вертикальная скорость изменяется так, что он начинает двигаться вниз (отрицательная величина <code>velY</code>).</p>
<p>В этих четырёх случаях мы:</p>
<ul>
<li>Проверяем больше ли координата <code>x</code>, чем ширина холста (мяч уходит с правого края).</li>
<li>Проверяем будет ли координата <code>x</code> меньше 0 (мяч уходит с левого края).</li>
<li>Проверяем будет ли координата <code>y</code> больше высоты холста (мяч уходит с нижнего края).</li>
<li>Проверяем будет ли координата <code>y</code> меньше 0 (мяч уходит с верхнего края).</li>
</ul>
<p>В каждом случае мы включаем <code>size</code> шарика в расчёт, потому что координаты <code>x</code>/<code>y</code> находятся в центре шара, но мы хотим, чтобы край шара отскакивал от периметра - мы не хотим, чтобы мяч на половину заходил за границу экрана прежде чем он начнёт возвращаться назад.</p>
<p>Последние две строки добавляют значение <code>velX</code> к координате <code>x</code>, а значение <code>velY</code> - координате <code>y</code> - шар фактически перемещается при каждом вызове этого метода.</p>
<p>На сейчас этого достаточно, давайте продолжим анимацию!</p>
<h2 id="Анимация_мяча">Анимация мяча</h2>
<p>Теперь давайте приступать к веселью! Сейчас мы начнём добавлять шары к холсту и анимировать их.</p>
<ol>
<li>Во-первых, нам нужно где-то хранить все наши шары. Следующий массив выполнит это задание - добавьте его внизу кода:
<pre class="brush: js notranslate">var balls = [];</pre>
<p>Все программы, которые оживляют вещи, обычно включают цикл анимации, который служит для обновления информации в программе, а затем визуализации результирующего представления для каждого кадра анимации; это основа для большинства игр и других подобных программ.</p>
</li>
<li>Добавьте ниже эту часть кода:
<pre class="brush: js notranslate">function loop() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
ctx.fillRect(0, 0, width, height);
while (balls.length < 25) {
var ball = new Ball(
random(0,width),
random(0,height),
random(-7,7),
random(-7,7),
'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')',
random(10,20)
);
balls.push(ball);
}
for (var i = 0; i < balls.length; i++) {
balls[i].draw();
balls[i].update();
}
requestAnimationFrame(loop);
}</pre>
<p>Наша функция <code>loop()</code> выполняет следующие действия:</p>
<ul>
<li>Устанавливает цвет заливки на полупрозрачный чёрный, затем рисует прямоугольник цвета по всей ширине и высоте холста, используя <code>fillRect()</code> (четыре параметра обеспечивают начальную координату, а ширину и высоту для рисованного прямоугольника ). Это позволяет скрыть рисунок предыдущего кадра до того, как будет нарисован следующий. Если вы этого не сделаете, вы увидите, как длинные змеи пробираются вокруг холста, а не шары! Цвет заливки устанавливается на полупрозрачный, <code>rgba(0,0,0,0,25)</code>, чтобы позволить нескольким кадрам слегка просвечивать, создавая маленькие тропы за шариками по мере их перемещения. Если вы изменили 0.25 на 1, вы больше не увидите их. Попробуйте изменить это число, чтобы увидеть эффект, который он имеет.</li>
<li>Создаёт новый экземпляр нашего <code>Ball()</code>, используя случайные значения, сгенерированные с помощью нашей функции <code>random()</code>, затем <code>push()</code> на конец нашего массива шаров, но только в том случае, когда количество шаров в массиве меньше 25. Итак когда у нас есть 25 мячей на экране, больше не появляется шаров. Вы можете попробовать изменить число в <code>balls.length < 25</code>, чтобы получить больше или меньше шаров на экране. В зависимости от того, сколько вычислительной мощности имеет ваш компьютер / браузер, если указать несколько тысяч шаров, это может довольно существенно повлиять на производительность анимации. </li>
<li>перебирает все шары в массиве <code>balls</code> и запускает каждую функцию <code>draw()</code> и <code>update()</code> для рисования каждого из них на экране, а затем выполняет необходимые обновления по положению и скорости во времени для следующего кадра.</li>
<li>Выполняет функцию снова с помощью метода <code>requestAnimationFrame()</code> - когда этот метод постоянно запускается и передаётся одно и то же имя функции, он будет запускать эту функцию определённое количество раз в секунду для создания плавной анимации. Обычно это делается рекурсивно - это означает, что функция вызывает себя каждый раз, когда она запускается, поэтому она будет работать снова и снова.</li>
</ul>
</li>
<li>И последнее, но не менее важное: добавьте следующую строку в конец вашего кода - нам нужно вызвать функцию один раз, чтобы начать анимацию.
<pre class="brush: js notranslate">loop();</pre>
</li>
</ol>
<p>Вот и все для основы - попробуйте сохранить и освежить, чтобы проверить свои прыгающие шары!</p>
<h2 id="Добавление_обнаружения_столкновений">Добавление обнаружения столкновений</h2>
<p>Теперь немного поиграем, давайте добавим в нашу программу обнаружение конфликтов, поэтому наши мячи узнают, когда они ударят по другому шару.</p>
<ol>
<li>Прежде всего, добавьте следующее определение метода ниже, где вы определили метод <code>update()</code> (т.е. блок <code>Ball.prototype.update</code>).
<pre class="brush: js notranslate">Ball.prototype.collisionDetect = function() {
for (var j = 0; j < balls.length; j++) {
if (!(this === balls[j])) {
var dx = this.x - balls[j].x;
var dy = this.y - balls[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + balls[j].size) {
balls[j].color = this.color = 'rgb(' + random(0, 255) + ',' + random(0, 255) + ',' + random(0, 255) +')';
}
}
}
}</pre>
<p>Этот метод немного сложный, поэтому не беспокойтесь, если вы не понимаете, как именно это работает. Ниже приводится объяснение:</p>
<ul>
<li>Для каждого шара нам нужно проверить каждый другой шар, чтобы увидеть, столкнулся ли он с текущим мячом. Чтобы сделать это, мы открываем ещё один цикл <code>for</code> через все шары в массиве <code>balls[]</code>.</li>
<li>Сразу же в нашем цикле for мы используем оператор <code>if</code>, чтобы проверить, проходит ли текущий шарик, тот же самый шар, что и тот, который мы сейчас проверяем. Мы не хотим проверять, что мяч столкнулся с самим собой! Для этого мы проверяем, является ли текущий мяч (т.е. мяч, метод которого вызван методом collisionDetect) такой же, как шар петли (т.е. шар, на который ссылается текущая итерация цикла for в collisionDetect метод). Затем мы используем <code>!</code> чтобы отменить проверку, чтобы код внутри оператора if выполнялся только в том случае, если они <strong>не</strong> совпадают.</li>
<li>Затем мы используем общий алгоритм для проверки столкновения двух окружностей. Мы в основном проверяем, перекрывается ли какая-либо из областей круга. Это объясняется далее <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection">2D collision detection</a>.</li>
<li>Если обнаружено столкновение, выполняется код внутри внутреннего оператора <code>if</code>. В этом случае мы просто устанавливаем свойство <code>color</code> обоих кругов на новый случайный цвет. Мы могли бы сделать что-то гораздо более сложное, например, заставить шары отскакивать друг от друга реалистично, но это было бы гораздо сложнее реализовать. Для такого моделирования физики разработчики склонны использовать игры или библиотеку физики, такие как <a href="http://wellcaffeinated.net/PhysicsJS/">PhysicsJS</a>, <a href="http://brm.io/matter-js/">matter.js</a>, <a href="http://phaser.io/">Phaser</a> и т.д.</li>
</ul>
</li>
<li>Вы также должны вызвать этот метод в каждом кадре анимации. Добавьте следующий код после строки <code>balls[i].update();</code>
<pre class="brush: js notranslate">balls[i].collisionDetect();</pre>
</li>
<li>Сохраните и обновите демо снова, и вы увидите, как ваши мячи меняют цвет, когда они сталкиваются!</li>
</ol>
<div class="note">
<p><strong>Примечание</strong>. Если вам не удаётся заставить этот пример работать, попробуйте сравнить код JavaScript с нашей <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/bouncing-balls/main-finished.js">готовой версией</a> (также смотрите, как он работает <a href="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html">в прямом эфире</a>).</p>
</div>
<h2 id="Резюме">Резюме</h2>
<p>Мы надеемся, что вам понравилось писать собственный пример случайных прыгающих шаров в реальном мире, используя различные объектные и объектно-ориентированные методы из всего модуля! Это должно было дать вам некоторую полезную практику использования объектов и хорошего контекста реального мира.</p>
<p>Вот и все для предметных статей - все, что осталось сейчас, - это проверить свои навыки в оценке объекта.</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<ul>
<li><a href="/en-US/docs/Web/API/Canvas_API/Tutorial">Canvas tutorial</a> — руководство для новичков по использованию 2D-холста.</li>
<li><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></li>
<li><a href="/en-US/docs/Games/Techniques/2D_collision_detection">2D collision detection</a></li>
<li><a href="/en-US/docs/Games/Techniques/3D_collision_detection">3D collision detection</a></li>
<li><a href="/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript">2D breakout game using pure JavaScript</a> — a great beginner's tutorial showing how to build a 2D game.</li>
<li><a href="/en-US/docs/Games/Tutorials/2D_breakout_game_Phaser">2D breakout game using Phaser</a> — explains the basics of building a 2D game using a JavaScript game library.</li>
</ul>
<p>{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}</p>
<h2 id="В_этом_модуле">В этом модуле</h2>
<ul>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B">Основы объекта</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/Object-oriented_JS">Объектно-ориентированный JavaScript для начинающих</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/Object_prototypes">Прототипы объектов</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/Inheritance">Наследование в JavaScript</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/JSON">Работа с данными JSON</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/Object_building_practice">Практика построения объектов</a></li>
<li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/Adding_bouncing_balls_features">Добавление функций в нашу демонстрацию прыгающих шаров</a></li>
</ul>
|