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
405
406
|
---
title: Object.create()
slug: Web/JavaScript/Reference/Global_Objects/Object/create
tags:
- ECMAScript 5
- JavaScript
- 'Null'
- Object
- метод
translation_of: Web/JavaScript/Reference/Global_Objects/Object/create
---
<div>{{JSRef}}</div>
<p>Метод <code><strong>Object.create()</strong></code> створює новий об'єкт, використовуючи існуючий об'єкт як прототип для новоствореного об'єкта.</p>
<div>{{EmbedInteractiveExample("pages/js/object-create.html")}}</div>
<h2 id="Синтаксис">Синтаксис</h2>
<pre class="syntaxbox">Object.create(<var>proto</var>, [<var>propertiesObject</var>])</pre>
<h3 id="Параметри">Параметри</h3>
<dl>
<dt><code><var>proto</var></code></dt>
<dd>Об'єкт, що має бути прототипом для новоствореного об'єкта.</dd>
<dt><code><var>propertiesObject</var></code><var> </var>{{Optional_inline}}</dt>
<dd>Якщо вказаний та не дорівнює {{jsxref("undefined")}}, об'єкт, чиї власні перелічувані властивості (тобто, властивості, визначені на самому об'єкті, а <em>не</em> перелічувані властивості, отримані через ланцюжок прототипів) визначають дескриптори властивостей, що мають бути додані до новоствореного об'єкта, з відповідними іменами властивостей. Ці властивості відповідають другому аргументу {{jsxref("Object.defineProperties()")}}.</dd>
</dl>
<h3 id="Значення_що_повертається">Значення, що повертається</h3>
<p>Новий об'єкт з зазначеним прототипом та властивостями.</p>
<h3 id="Винятки">Винятки</h3>
<p>Виняток {{jsxref("TypeError")}}, якщо параметр <code>propertiesObject</code> дорівнює {{jsxref("null")}} або це об'єкт, що не є обгорткою простого типу.</p>
<h2 id="Приклади">Приклади</h2>
<h3 id="Класичне_наслідування_через_Object.create">Класичне наслідування через <code>Object.create()</code></h3>
<p>Нижче наведено приклад використання <code>Object.create()</code> для отримання класичного наслідування. Це приклад одиночного наслідування, єдиного, яке підтримує JavaScript.</p>
<pre class="brush: js">// Shape - батьківський клас
function Shape() {
this.x = 0;
this.y = 0;
}
// метод батьківського класу
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Фігуру переміщено.');
};
// Rectangle - дочірній клас
function Rectangle() {
Shape.call(this); // виклик батьківського конструктора.
}
// дочірній клас розширює батьківській клас
Rectangle.prototype = Object.create(Shape.prototype);
//Якщо ви не призначите Object.prototype.constructor значення Rectangle,
//він візьме prototype.constructor класу Shape (батьківського).
//Щоб уникнути цього, ми призначаємо prototype.constructor значення Rectangle (дочірній).
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Чи є rect екземпляром Rectangle?', rect instanceof Rectangle); // true
console.log('Чи є rect екземпляром Shape?', rect instanceof Shape); // true
rect.move(1, 1); // Виводить 'Фігуру переміщено.'
</pre>
<p>Якщо ви бажаєте успадкуватись від кількох об'єктів, можна використати домішки.</p>
<pre class="brush: js">function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// успадкувати від одного класу
MyClass.prototype = Object.create(SuperClass.prototype);
// змішати з іншим
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// перепризначити конструктор
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// зробити щось
};
</pre>
<p>Метод {{jsxref("Object.assign()")}} копіює властивості з прототипу OtherSuperClass у прототип MyClass, роблячи їх доступними для усіх екземплярів MyClass. <code>Object.assign()</code> був запроваджений у ES2015 і <a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Запасний_варіант_(поліфіл)">може бути відтворений поліфілом</a>. Якщо необхідна підтримка більш старих переглядачів, можна використати <code><a href="https://api.jquery.com/jQuery.extend/">jQuery.extend()</a></code> або <code><a href="https://lodash.com/docs/#assign">_.assign()</a></code>.</p>
<h3 id="Використання_аргументу_propertiesObject_у_Object.create">Використання аргументу <code>propertiesObject</code> у <code>Object.create()</code></h3>
<pre class="brush: js">var o;
// створити об'єкт з прототипом null
o = Object.create(null);
o = {};
// є еквівалентом:
o = Object.create(Object.prototype);
// У цьому прикладі ми створюємо об'єкт з парою
// зразкових властивостей. (Зауважте, що другий параметр
// встановлює ключі у відповідності до *дескрипторів властивості*.)
o = Object.create(Object.prototype, {
// foo - це звичайна властивість-значення
foo: {
writable: true,
configurable: true,
value: 'hello'
},
// bar - це властивість-аксесор (має гетер та сетер)
bar: {
configurable: false,
get: function() { return 10; },
set: function(value) {
console.log('Призначення `o.bar` значення', value);
}
/* з аксесорами ES2015 наш код може виглядати так
get() { return 10; },
set(value) {
console.log('Призначення `o.bar` значення', value);
} */
}
});
function Constructor() {}
o = new Constructor();
// є еквівалентом:
o = Object.create(Constructor.prototype);
// Звісно, якщо у функції Constructor присутній
// код ініціалізації,
// Object.create() не може його відобразити
// Створити новий об'єкт, чиїм прототипом є новий, порожній
// об'єкт, та додати єдину властивість 'p' зі значенням 42.
o = Object.create({}, { p: { value: 42 } });
// за замовчуванням властивості НЕДОСТУПНІ для запису,
// переліку чи налаштування:
o.p = 24;
o.p;
// 42
o.q = 12;
for (var prop in o) {
console.log(prop);
}
// 'q'
delete o.p;
// false
// щоб визначити властивість у ES3
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
/* є еквівалентом:
o2 = Object.create({p: 42}) */
</pre>
<h2 id="Користувацькі_та_нульові_обєкти">Користувацькі та нульові об'єкти</h2>
<p>Новий об'єкт, створений на основі користувацького об'єкта (особливо об'єкт, створений на основі об'єкта <code>null</code>, який по суті є користувацьким об'єктом, що НЕ МАЄ членів), може поводитись неочікувано. Особливо під час налагодження, оскільки звичайні функції утиліт для перетворення/видалення об'єктних властивостей можуть генерувати помилки або просто втрачати інформацію (особливо якщо використовують перехоплювачі помилок, що ігнорують помилки). Наприклад, ось два об'єкти:</p>
<pre class="brush: js">oco = Object.create( {} ); // створити нормальний об'єкт
ocn = Object.create( null ); // створити "нульовий" об'єкт
> console.log(oco) // {} -- Виглядає нормально
> console.log(ocn) // {} -- Поки що теж виглядає нормально
oco.p = 1; // створити просту властивість на нормальному об'єкті
ocn.p = 0; // створити просту властивість на "нульовому" об'єкті
> console.log(oco) // {p: 1} -- Досі виглядає нормально
> console.log(ocn) // {p: 0} -- Теж виглядає нормально. АЛЕ СТРИВАЙТЕ...
</pre>
<p><br>
Як показано вище, поки що все виглядає нормальним. Однак, при спробі використати ці об'єкти, їхні відмінності швидко стають очевидними:</p>
<pre class="brush: js">> "oco is: " + oco // виводить "oco is: [object Object]"
> "ocn is: " + ocn // викидає помилку: Cannot convert object to primitive value
</pre>
<p>Перевірка лише декількох з багатьох базових вбудованих функцій більш чітко демонструє величину проблеми:</p>
<pre class="brush: js">> alert(oco) // виводить [object Object]
> alert(ocn) // викидає помилку: Cannot convert object to primitive value
> oco.toString() // виводить [object Object]
> ocn.toString() // викидає помилку: ocn.toString is not a function
> oco.valueOf() // виводить {}
> ocn.valueOf() // викидає помилку: ocn.valueOf is not a function
> oco.hasOwnProperty("p") // виводить "true"
> ocn.hasOwnProperty("p") // викидає помилку: ocn.hasOwnProperty is not a function
> oco.constructor // виводить "Object() { [native code] }"
> ocn.constructor // виводить "undefined"
</pre>
<p><br>
Як вже сказано, ці відмінності можуть швидко зробити процес відлагодження навіть простих на вигляд проблем дуже заплутаним. Наприклад:</p>
<p><em>Проста звичайна функція налагодження:</em></p>
<pre class="brush: js">// вивести пари ключ-значення властивостей верхнього рівня наданого об'єкта
function ShowProperties(obj){
for(var prop in obj){
console.log(prop + ": " + obj[prop] + "\n" )
}
}</pre>
<p><br>
<em>Не такі прості результати: (особливо якщо перехоплювач помилок сховав повідомлення про помилки)</em></p>
<pre class="brush: js">ob={}; ob.po=oco; ob.pn=ocn; // створити складний об'єкт з наданих вище тестових об'єктів в якості значень властивостей
> ShowProperties( ob ) // вивести властивості верхнього рівня
- po: [object Object]
- Error: Cannot convert object to primitive value
Зауважте, що виводиться тільки перша властивість.
</pre>
<p><br>
<em>(Але якщо такий самий об'єкт був просто створений в іншому порядку -- принаймні, в деяких реалізаціях...)</em></p>
<pre class="brush: js">ob={}; ob.pn=ocn; ob.po=oco; // створити знову такий самий об'єкт, але створити ті самі властивості в іншому порядку
> ShowProperties( ob ) // вивести властивості верхнього рівня
- Error: Cannot convert object to primitive value
Зауважте, що жодна властивість не виводиться.</pre>
<p>Зауважте, що такий відмінний порядок може виникнути статично, через відмінний зафіксований код, як ось тут, а може й динамічно, через порядок, в якому виконуються під час запуску гілки коду з додаванням властивостей, що залежить від вхідних даних та/або випадкових змінних. Знову ж таки, реальний порядок перебору не гарантований, в якому б порядку не додавалися члени об'єкта.</p>
<h4 id="Деякі_рішення_що_не_працюють">Деякі рішення, що не працюють</h4>
<p>Гарне вирішення проблеми відсутніх об'єктних методів не завжди є очевидним.</p>
<p>Пряме додавання відсутнього об'єктного метода зі стандартного об'єкта НЕ працює:</p>
<pre class="brush: js">ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
ocn.toString = Object.toString; // оскільки йому бракує методу, призначити його прямо зі стандартного об'єкта
<span style="letter-spacing: -0.00333rem;">> ocn.toString // виводить "toString() { [native code] }" -- схоже, що відсутній метод тепер додано</span>
> ocn.toString == Object.toString // виводить "true" -- схоже, це той самий метод, що й у стандартному об'єкті
> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function
</pre>
<p><br>
Пряме додавання відсутнього об'єктного метода у "прототип" нового об'єкта також не працює, оскільки у нового об'єкта немає справжнього прототипа (що й є справжньою причиною УСІХ цих проблем), і його не можна додати <strong>прямо</strong>:</p>
<pre class="brush: js">ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
ocn.prototype.toString = Object.toString; // Error: Cannot set property 'toString' of undefined
ocn.prototype = {}; // спробувати створити прототип
ocn.prototype.toString = Object.toString; // оскільки об'єкту бракує методу, призначити його прямо зі стандартного об'єкта<span style="letter-spacing: -0.00333rem;">
> ocn.toString() // error: ocn.toString is not a function</span>
</pre>
<p><br>
Додавання відсутнього об'єктного метода використанням стандартного об'єкта в якості прототипа нового об'єкта також не працює:</p>
<pre class="brush: js">ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
Object.setPrototypeOf(ocn, Object); // встановити значенням прототипу нового об'єкта стандартний об'єкт
> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function
</pre>
<h4 id="Деякі_вдалі_рішення">Деякі вдалі рішення</h4>
<p>Як вже сказано, пряме додавання відсутнього об'єктного методу зі <strong>стандартного об'єкта </strong>НЕ працює. Однак, пряме додавання <strong>загального</strong> метода ПРАЦЮЄ:</p>
<pre class="brush: js">ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
ocn.toString = toString; // оскільки об'єкту бракує методу, призначити його прямо з загальної версії
> ocn.toString() // виводить "[object Object]"
> "ocn is: " + ocn // виводить "ocn is: [object Object]"
ob={}; ob.pn=ocn; ob.po=oco; // створити складний об'єкт (такий самий, як і раніше)
> ShowProperties(ob) // вивести властивості верхнього рівня
- po: [object Object]
- pn: [object Object]
</pre>
<p>Однак, встановлення загального <strong>прототипу</strong> прототипом нового об'єкта працює навіть краще:</p>
<pre class="brush: js">ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
Object.setPrototypeOf(ocn, Object.prototype); // встановити значенням прототипу нового об'єкта "загальний" об'єкт (НЕ стандартний об'єкт)
</pre>
<p><em>(На додачу до функцій, пов'язаних з рядками, що наведені вище, це також додає:)</em></p>
<pre class="brush: js">> ocn.valueOf() // виводить {}
> ocn.hasOwnProperty("x") // виводить "false"
> ocn.constructor // виводить "Object() { [native code] }"
// ...та вся решта властивостей та методів Object.prototype.
</pre>
<p>Як бачимо, об'єкти, змінені таким чином, тепер виглядають дуже схожими на звичайні об'єкти.</p>
<h2 id="Поліфіл">Поліфіл</h2>
<p>Цей поліфіл покриває основний сценарій використання, а саме, створення нового об'єкта, для якого був обраний прототип, але не бере до уваги другий аргумент.</p>
<p>Зауважте, що в той час як використання <code>null</code> в якості <code>[[Prototype]]</code> підтримується в реальному методі ES5 <code>Object.create</code>, цей поліфіл не може це підтримувати через обмеження, притаманне версіям ECMAScript нижче 5.</p>
<pre class="brush: js"> if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError("Прототипом об'єкта може бути тільки об'єкт: " + proto);
} else if (proto === null) {
throw new Error("Реалізація Object.create у цьому браузері є шимом та не підтримує 'null' в якості першого аргументу.");
}
if (typeof propertiesObject != 'undefined') {
throw new Error("Реалізація Object.create у цьому браузері є шимом та не підтримує другий аргумент.");
}
function F() {}
F.prototype = proto;
return new F();
};
}
</pre>
<h2 id="Специфікації">Специфікації</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">Специфікація</th>
<th scope="col">Статус</th>
<th scope="col">Коментар</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('ES5.1', '#sec-15.2.3.5', 'Object.create')}}</td>
<td>{{Spec2('ES5.1')}}</td>
<td>Початкове визначення. Реалізоване у JavaScript 1.8.5.</td>
</tr>
<tr>
<td>{{SpecName('ES2015', '#sec-object.create', 'Object.create')}}</td>
<td>{{Spec2('ES2015')}}</td>
<td></td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-object.create', 'Object.create')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="Сумісність_з_веб-переглядачами">Сумісність з веб-переглядачами</h2>
<div>
<p>{{Compat("javascript.builtins.Object.create")}}</p>
</div>
<h2 id="Див._також">Див. також</h2>
<ul>
<li>{{jsxref("Object.defineProperty()")}}</li>
<li>{{jsxref("Object.defineProperties()")}}</li>
<li>{{jsxref("Object.prototype.isPrototypeOf()")}}</li>
<li>{{jsxref("Reflect.construct()")}}</li>
<li>John Resig's post on <a href="http://ejohn.org/blog/objectgetprototypeof/">getPrototypeOf()</a></li>
</ul>
|