aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/javascript/reference/global_objects/object/defineproperty/index.html
blob: 54b342bb891e8ecba2388bbeb9e9f55482661297 (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
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
---
title: Object.defineProperty()
slug: Web/JavaScript/Reference/Global_Objects/Object/defineProperty
tags:
  - ECMAScript5
  - JavaScript
  - JavaScript 1.8.5
  - Method
  - Object
  - Reference
  - Référence(2)
translation_of: Web/JavaScript/Reference/Global_Objects/Object/defineProperty
---
<div>{{JSRef("Global_Objects", "Object")}}</div>

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

<p>Метод <code><strong>Object.defineProperty()</strong></code> определяет новое или изменяет существующее свойство непосредственно на объекте, возвращая этот объект.</p>

<h2 id="Syntax" name="Syntax">Синтаксис</h2>

<pre class="syntaxbox"><code>Object.defineProperty(<var>obj</var>, <var>prop</var>, <var>descriptor</var>)</code></pre>

<h3 id="Parameters" name="Parameters">Параметры</h3>

<dl>
 <dt><code>obj</code></dt>
 <dd>Объект, на котором определяется свойство.</dd>
 <dt><code>prop</code></dt>
 <dd>Имя определяемого или изменяемого свойства.</dd>
 <dt><code>descriptor</code></dt>
 <dd>Дескриптор определяемого или изменяемого свойства.</dd>
</dl>

<h2 id="Description" name="Description">Описание</h2>

<p>Этот метод позволяет точно добавлять или изменять свойства объекта. Обычное добавление свойств через присваивание создаёт свойства, которые можно увидеть через перечисление свойств (с помощью цикла {{jsxref("Statements/for...in", "for...in")}} или метода {{jsxref("Object.keys")}}), чьи значения могут быть изменены и которые могут быть {{jsxref("Operators/delete", "удалены", "", 1)}}. Этот же метод позволяет настроить эти дополнительные детали свойства.</p>

<p>Дескрипторы свойств, присутствующие в объектах, бывают двух основных типов: дескрипторы данных и дескрипторы доступа. <em><dfn>Дескриптор данных</dfn></em> — это свойство, имеющее значение, которое может быть (а может и не быть) записываемым. <em><dfn>Дескриптор доступа</dfn></em> — это свойство, описываемое парой функций — геттером и сеттером. Дескриптор может быть только чем-то одним из этих двух типов; он не может быть одновременно обоими.</p>

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

<dl>
 <dt><code>configurable</code></dt>
 <dd>Равен <code>true</code> только в том случае, если тип этого дескриптора свойства может быть изменён и если свойство может быть удалено из содержащего его объекта.<br>
 <strong>Значение по умолчанию установлено в <code>false</code>.</strong></dd>
 <dt><code>enumerable</code></dt>
 <dd>Равен <code>true</code> только в том случае, если это свойство можно увидеть через перечисление свойств содержащего его объекта.<br>
 <strong>Значение по умолчанию установлено в <code>false</code>.</strong></dd>
</dl>

<p>Дескриптор данных также может содержать следующие дополнительные ключи:</p>

<dl>
 <dt><code>value</code></dt>
 <dd>Значение, ассоциированное со свойством. Может быть любым допустимым значением JavaScript (числом, объектом, функцией и т.д.).<br>
 <strong>Значение по умолчанию установлено в {{jsxref("Global_Objects/undefined", "undefined")}}.</strong></dd>
 <dt><code>writable</code></dt>
 <dd>Равен <code>true</code> только в том случае, если значение, ассоциированное со свойством, может быть изменено с помощью {{jsxref("Operators/Assignment_Operators", "оператора присваивания", "", 1)}}.<br>
 <strong>Значение по умолчанию установлено в <code>false</code>.</strong></dd>
</dl>

<p>Дескриптор доступа также может содержать следующие дополнительные ключи:</p>

<dl>
 <dt><code>get</code></dt>
 <dd>Функция, используемая как геттер свойства, либо {{jsxref("Global_Objects/undefined", "undefined")}}, если свойство не имеет геттера. Возвращаемое значение функции будет использоваться как значение свойства.<br>
 <strong>Значение по умолчанию установлено в {{jsxref("Global_Objects/undefined", "undefined")}}.</strong></dd>
 <dt><code>set</code></dt>
 <dd>Функция, используемая как сеттер свойства, либо {{jsxref("Global_Objects/undefined", "undefined")}}, если свойство не имеет сеттера. Функция принимает единственным аргументом новое значение, присваиваемое свойству.<br>
 <strong>Значение по умолчанию установлено в {{jsxref("Global_Objects/undefined", "undefined")}}.</strong></dd>
</dl>

<p>Имейте в виду, что эти ключи не обязательно должны принадлежать самому дескриптору свойства, если они унаследованы, они так же будут приниматься во внимание. Для сохранения этих ключей по умолчанию неизменными, вы можете заранее заморозить {{jsxref("Object.prototype")}}, явно определив все ключи, либо установить свойство {{jsxref("Object.prototype.__proto__", "__proto__")}} в {{jsxref("Global_Objects/null", "null")}}.</p>

<pre class="brush:js">// Использование __proto__
Object.defineProperty(obj, 'key', {
  __proto__: null, // нет унаследованных свойств
  value: 'static'  // по умолчанию
                   // не перечисляется,
                   // не настраивается и
                   // не перезаписывается
});

// Явное определение свойства
Object.defineProperty(obj, 'key', {
  enumerable: false,
  configurable: false,
  writable: false,
  value: 'static'
});

// Переиспользование одного и того же объекта
function withValue(value) {
  var d = withValue.d || (
    withValue.d = {
      enumerable: false,
      writable: false,
      configurable: false,
      value: null
    }
  );
  d.value = value;
  return d;
}
// ... и ...
Object.defineProperty(obj, 'key', withValue('static'));

// Если доступен метод freeze, предотвращаем добавление свойств
// value, get, set, enumerable, writable и configurable
// к прототипу Object
(Object.freeze || Object)(Object.prototype);
</pre>

<h2 id="Examples" name="Examples">Примеры</h2>

<p>Если вы хотите посмотреть, как использовать метод <code>Object.defineProperty()</code> с синтаксисом <em>похожим-на-бинарные-флаги</em>, смотрите <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty/Additional_examples">дополнительные примеры</a>.</p>

<h3 id="Example:_Creating_a_property" name="Example:_Creating_a_property">Пример: создание свойства</h3>

<p>Если указанное свойство не существует в объекте, метод <code>Object.defineProperty()</code> создаст новое свойство по переданному описанию. Поля в дескрипторе могут быть опущены, в этом случае их значения будут значениями по умолчанию. Все логические поля будут по умолчанию установлены в <code>false</code>. Поля <code>value</code>, <code>get</code> и <code>set</code> по умолчанию будут установлены в {{jsxref("Global_Objects/undefined", "undefined")}}. Свойство, определённое без атрибутов <code>get</code>/<code>set</code>/<code>value</code>/<code>writable</code> называется «общим», а дескриптор данных — «типовым».</p>

<pre class="brush: js">var o = {}; // Создаём новый объект

// Пример добавления свойства к объекту через defineProperty()
// с дескриптором данных
Object.defineProperty(o, 'a', {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true
});
// Свойство 'a' существует в объекте o и имеет значение, равное 37

// Пример добавления свойства к объекту через defineProperty()
// с дескриптором доступа
var bValue = 38;
Object.defineProperty(o, 'b', {
  get: function() { return bValue; },
  set: function(newValue) { bValue = newValue; },
  enumerable: true,
  configurable: true
});
o.b; // 38
// Свойство 'b' существует в объекте o и имеет значение, равное 38
// Значение свойства o.b теперь идентично значению bValue до тех пор,
// пока свойство o.b не будет переопределено

// Вы не можете смешать два этих подхода:
Object.defineProperty(o, 'conflict', {
  value: 0x9f91102,
  get: function() { return 0xdeadbeef; }
});
// Выкинет исключение TypeError: свойство value применимо только в
// дескрипторах данных, свойство get применимо только в дескрипторах
// доступа
</pre>

<h3 id="Example:_Modifying_a_property" name="Example:_Modifying_a_property">Пример: изменение свойства</h3>

<p>Если свойство уже существует, метод <code>Object.defineProperty()</code> попытается изменить свойство в соответствии со значениями в дескрипторе и текущим состоянием объекта. Если у старого дескриптора атрибут <code>configurable</code> был установлен в <code>false</code> (говорит, что свойство является «ненастраиваемым»), то никакие атрибуты, кроме атрибута <code>writable</code>, не смогут быть изменены. В этом случае так же невозможно переключаться между типами дескрипторов.</p>

<p>Если свойство является ненастраиваемым, его атрибут <code>writable</code> может быть изменён только на значение <code>false</code>.</p>

<p>При попытке изменить ненастраиваемые атрибуты свойства будет выброшено исключение {{jsxref("Global_Objects/TypeError", "TypeError")}} (кроме случая изменения атрибута <code>writable</code>), даже в том случае, если текущее и новое значения идентичны.</p>

<h4 id="Writable_attribute" name="Writable_attribute">Атрибут <code>writable</code></h4>

<p>Если атрибут <code>writable</code> свойства установлен в <code>false</code>, свойство становится «незаписываемым». Ему невозможно будет присвоить новое значение.</p>

<pre class="brush: js">var o = {}; // Создаём новый объект

Object.defineProperty(o, 'a', {
  value : 37,
  writable : false
});

console.log(o.a); // Выведет 37
o.a = 25; // Исключение не будет выброшено (будет выброшено только в
          // строгом режиме, даже если значение будет тем же самым)
console.log(o.a); // Выведет 37. Присваивание не сработало.
</pre>

<p>Как видно в этом примере, попытка записи в незаписываемое свойство его не изменило, но и исключение так же не было выброшено.</p>

<h4 id="Enumerable_attribute" name="Enumerable_attribute">Атрибут <code>enumerable</code></h4>

<p>Атрибут <code>enumerable</code> свойства определяет, просматривается ли свойство в цикле {{jsxref("Statements/for...in", "for...in")}} и методом {{jsxref("Object.keys()")}} или нет.</p>

<pre class="brush: js">var o = {};
Object.defineProperty(o, 'a', { value: 1, enumerable: true });
Object.defineProperty(o, 'b', { value: 2, enumerable: false });
// Атрибут enumerable по умолчанию установлен в false
Object.defineProperty(o, 'c', { value: 3 });

o.d = 4; // При создании свойства путём присваивания, атрибут enumerable
         // по умолчанию установливается в true

for (var i in o) {
  console.log(i);
}
// Выведет 'a' и 'd' (порядок вывода не определён)

Object.keys(o); // ['a', 'd']

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
</pre>

<h4 id="Configurable_attribute" name="Configurable_attribute">Атрибут <code>configurable</code></h4>

<p>Атрибут <code>configurable</code> одновременно контролирует, может ли свойство быть удалено из объекта и могут ли быть изменены его атрибуты (кроме контроля изменения атрибута <code>writable</code>).</p>

<pre class="brush: js">var o = {};
Object.defineProperty(o, 'a', {
  get: function() { return 1; },
  configurable: false
});

Object.defineProperty(o, 'a', { configurable: true }); // Выкинет TypeError
Object.defineProperty(o, 'a', { enumerable: true }); // Выкинет TypeError
Object.defineProperty(o, 'a', { set: function() {} }); // Выкинет TypeError (ранее свойство set дескриптора не было определено)
Object.defineProperty(o, 'a', { get: function() { return 1; } }); // Выкинет TypeError (даже несмотря на то, что get делает то же, что и раньше)
Object.defineProperty(o, 'a', { value: 12 }); // Выкинет TypeError

console.log(o.a); // Выведет 1
delete o.a; // Ничего не произойдёт
console.log(o.a); // Выведет 1
</pre>

<p>Если бы атрибут <code>configurable</code> объекта <code>o.a</code> был установлен в <code>true</code>, никакие ошибки не были бы выброшены и в конце свойство было бы удалено.</p>

<h3 id="Example:_Adding_properties_and_default_values" name="Example:_Adding_properties_and_default_values">Пример: добавление свойств и значений по умолчанию</h3>

<p>Важно понимать, как устанавливаются значения по умолчанию атрибутам свойств. Часто существует разница между простым назначением значения посредством точечной нотации и использованием метода <code>Object.defineProperty()</code>, что и показывает пример ниже.</p>

<pre class="brush: js">var o = {};

o.a = 1;
// Эквивалентно записи:
Object.defineProperty(o, 'a', {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true
});


// С другой стороны,
Object.defineProperty(o, 'a', { value: 1 });
// эквивалентно записи:
Object.defineProperty(o, 'a', {
  value: 1,
  writable: false,
  configurable: false,
  enumerable: false
});
</pre>

<h3 id="Example:_Custom_setters_and_getters" name="Example:_Custom_setters_and_getters">Пример: пользовательские сеттеры и геттеры</h3>

<p>Пример ниже показывает, как реализовать самоархивирующийся объект. При установке свойства <code>temperature</code> в массив <code>archive</code> попадает запись журнала.</p>

<pre class="brush: js">function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
</pre>

<h2 id="Specifications" name="Specifications">Спецификации</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">Спецификация</th>
   <th scope="col">Статус</th>
   <th scope="col">Комментарии</th>
  </tr>
  <tr>
   <td>{{SpecName('ES5.1', '#sec-15.2.3.6', 'Object.defineProperty')}}</td>
   <td>{{Spec2('ES5.1')}}</td>
   <td>Изначальное определение. Реализована в JavaScript 1.8.5.</td>
  </tr>
  <tr>
   <td>{{SpecName('ES6', '#sec-object.defineproperty', 'Object.defineProperty')}}</td>
   <td>{{Spec2('ES6')}}</td>
   <td> </td>
  </tr>
 </tbody>
</table>

<h2 id="Browser_compatibility" name="Browser_compatibility">Совместимость с браузерами</h2>

<div>{{CompatibilityTable}}</div>

<div id="compat-desktop">
<table class="compat-table">
 <tbody>
  <tr>
   <th>Возможность</th>
   <th>Firefox (Gecko)</th>
   <th>Chrome</th>
   <th>Internet Explorer</th>
   <th>Opera</th>
   <th>Safari</th>
  </tr>
  <tr>
   <td>Базовая поддержка</td>
   <td>{{CompatGeckoDesktop("2")}}</td>
   <td>{{CompatChrome("5")}} (предыдущие версии не проверялись)</td>
   <td>{{CompatIE("9")}} ({{CompatIE("8")}}, но только на объектах DOM с нестандартным поведением. <a href="#Internet_Explorer_8_specific_notes">Подробности смотрите ниже</a>.)</td>
   <td>{{CompatOpera("11.60")}}</td>
   <td>{{CompatSafari("5.1")}} ({{CompatSafari("5")}}, кроме объектов DOM)</td>
  </tr>
 </tbody>
</table>
</div>

<div id="compat-mobile">
<table class="compat-table">
 <tbody>
  <tr>
   <th>Возможность</th>
   <th>Firefox Mobile (Gecko)</th>
   <th>Android</th>
   <th>IE Mobile</th>
   <th>Opera Mobile</th>
   <th>Safari Mobile</th>
  </tr>
  <tr>
   <td>Базовая поддержка</td>
   <td>{{CompatGeckoMobile("2")}}</td>
   <td>{{CompatVersionUnknown}}</td>
   <td>{{CompatIE("9")}} и выше</td>
   <td>{{CompatOperaMobile("11.50")}}</td>
   <td>{{CompatVersionUnknown}}</td>
  </tr>
 </tbody>
</table>
</div>

<p>На основе <a class="external" href="http://kangax.github.com/es5-compat-table/">таблицы совместимости Kangax</a>.</p>

<h3 id="Redefining_the_length_property_of_an_Array_object" name="Redefining_the_length_property_of_an_Array_object">Переопределение свойства <code>length</code> на объекте <code>Array</code></h3>

<p>На массивах возможно переопределить свойство {{jsxref("Array.length", "length")}}, при условии соблюдения обычных ограничений на переопределение. (Изначально свойство {{jsxref("Array.length", "length")}} является ненастраиваемым, неперечисляемым и записываемым. Таким образом, на неизмененном массиве возможно изменить значение свойства {{jsxref("Array.length", "length")}} либо сделать его незаписываемым. Его перечисляемость или настраиваемость изменить нельзя, так же как и его записываемость, если оно сделано не записываемым.) Однако не все браузеры поддерживают такое переопределение.</p>

<p>Firefox с версии 4 по версию 22 бросает исключение {{jsxref("Global_Objects/TypeError", "TypeError")}} на любую попытку (вне зависимости от того, разрешена ли она или нет) переопределить свойство {{jsxref("Array.length", "length")}} массива.</p>

<p>Версии Chrome, реализующие метод <code>Object.defineProperty()</code>, в некоторых случаях игнорируют значение <code>length</code> отличное от текущего значения свойства {{jsxref("Array.length", "length")}} массива. В некоторых случаях изменение записываемости свойства просто не работает без каких-либо сообщений об ошибках (и выбрасываемых исключений). Также, в связи с вышесказанным, некоторые методы изменения массива, вроде метода {{jsxref("Array.prototype.push")}}, не обращают внимания на незаписываемое свойство {{jsxref("Array.length", "length")}}.</p>

<p>Версии Safari, реализующие метод <code>Object.defineProperty()</code>, игнорируют значение <code>length</code>, отличное от текущего значения свойства {{jsxref("Array.length", "length")}} массива, и попытка изменить записываемость свойства выполнится без ошибок, но на самом деле записываемость свойства не изменится.</p>

<p>Только Internet Explorer 9 и выше, а так же Firefox 23 и выше, по-видимому, полностью и правильно реализуют переопределение свойства {{jsxref("Array.length", "length")}} массивов. В настоящее время не стоит полагаться на работу переопределения свойства {{jsxref("Array.length", "length")}} массива, или что оно работает определённым образом. И даже если вы <em>можете</em> положиться на это, <a href="http://whereswalden.com/2013/08/05/new-in-firefox-23-the-length-property-of-an-array-can-be-made-non-writable-but-you-shouldnt-do-it/">есть действительно веская причина не делать этого</a>.</p>

<h3 id="Internet_Explorer_8_specific_notes" name="Internet_Explorer_8_specific_notes">Примечания по Internet Explorer 8</h3>

<p>Реализация метода <code>Object.defineProperty()</code> в Internet Explorer 8 предусматривает, что он может быть <a class="external" href="http://msdn.microsoft.com/en-us/library/dd229916%28VS.85%29.aspx">использован только для объектов DOM</a>. Нужно также отметить несколько моментов:</p>

<ul>
 <li>Попытка использовать <code>Object.defineProperty()</code> на родных объектах приведёт к ошибке.</li>
 <li>Атрибуты свойств должны быть равны определённым значениям. Атрибуты <code>configurable</code>, <code>enumerable</code> и <code>writable</code> дескриптора данных должны содержать значения <code>true</code>, <code>true</code> и <code>true</code>, а атрибуты <code>configurable</code> и <code>enumerable</code> дескриптора доступа должны содержать значения <code>true</code> и <code>false</code> соответственно.(?) Любая попытка присвоить атрибутам другие значения приведёт к ошибке.</li>
 <li>Для того, чтобы изменить конфигурацию свойства, сначала его нужно удалить. Если свойство не будет удалено, оно останется в том же виде, что и до попытки изменения его конфигурации.</li>
</ul>

<h2 id="See_also" name="See_also">Смотрите также</h2>

<ul>
 <li><a href="/ru/docs/Enumerability_and_ownership_of_properties">Перечисляемость и собственность свойств</a></li>
 <li>{{jsxref("Object.defineProperties()")}}</li>
 <li>{{jsxref("Object.propertyIsEnumerable()")}}</li>
 <li>{{jsxref("Object.getOwnPropertyDescriptor()")}}</li>
 <li>{{jsxref("Object.prototype.watch()")}}</li>
 <li>{{jsxref("Object.prototype.unwatch()")}}</li>
 <li>{{jsxref("Operators/get", "get")}}</li>
 <li>{{jsxref("Operators/set", "set")}}</li>
 <li>{{jsxref("Object.create()")}}</li>
 <li><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty/Additional_examples">Дополнительные примеры по <code>Object.defineProperty()</code></a></li>
</ul>