Introduced in JavaScript 1.8.5
Добавляет новое свойство к объекту или изменяет существующее и возвращает объект.
Method of Object |
|
|---|---|
| Реализовано в | JavaScript 1.8.5 |
| ECMAScript Edition | ECMAScript 5th Edition |
Синтаксис
Object.defineProperty(obj, prop, descriptor)
Параметры
-
obj - Объект, для которого определяется свойство.
-
prop - Имя свойства, которое нужно добавить или изменить.
-
descriptor - Описание свойства, которое добавляется или изменяется.
Описание
Этот метод позволяет добавлять новые или изменять существующие свойства объекта. Обычно, добавление нового свойства производится через присвоение: оно создает свойство, которое доступно во время перечисления свойств (for...in loop или Object.keys method), а также быть изменено или удалено. Этот метод (Object.defineProperty)позволяет изменить те расширенные детали свойсва, которые отвечают за доступность во время перечисления и в цикле (for...in)или при использовании метода Object.keys,и назначить их не такими как они устанавливаюся по умолчанию.
Описание свойства, существующее в объекте, получаются двумя основными путями: при помощи описателeй данных (data descriptors) или описателей доступа (accessor descriptors(accessor-досупатор, если дословно)). Описатель данных (data descriptors) - свойство имеющее значение, которое может быть либо перезаписываемым, либо нет. Описатель доступа (accessor descriptors) - совойство описанное через пару getter-setter (получетель-установщик) функции. Важно то, что описатели могут быть только одного типа, не могут быть использованы сразу оба.
Оба описателя данных и доступа - это объекты со следующими дополнительными ключами:
configurable
true, если хотя бы один из дескрипторов свойства может быть изменён или само свойство может быть удалено из содержащего его объекта. По-умолчанию false.
-
enumerable -
true, если свойство объекта следует отображать при переборе свойств содержащего его объекта. По-умолчаниюfalse.
Дескриптор данных объекта может быть объявлен со следующими необязательными ключами:
-
value -
Значение, связанное со свойством. Может быть любым корректным JavaScript-значением (число, объект, функция и т.д.). По-умолчанию
undefined. -
writable -
true, если значение свойства может быть изменено с помощью оператора присваивания. По-умолчаниюfalse.
Дескриптор доступа может содержать следующие необязательные ключи:
-
get -
Функция, используемая как геттер для свойства, либо
undefined. Возвращаемое значение функции используется как значение свойства. По-умолчаниюundefined. -
set -
Функция, используемая в качестве сеттера свойства, или
undefined. Должна принимать один аргумент, который будет использоваться как значение свойства. По-умолчаниюundefined.
Создание свойства
Когда определенное свойство не существует в объекте, Object.defineProperty () создает новое свойство, как описано. Поля могут быть опущены в дескрипторе, и значения по умолчанию для этих полей назначаются по умолчанию. Все логические поля по умолчанию равны false. value, get, и set по умолчанию не определены(равны undefined). Свойство, которое объявляется без get/set/value/writable называется "общим"("generic") и "типизировынным"("typed") как дескриптор данных.
Пример
var o = {}; // Создание нового объекта
// Пример: свойство объекта добавлено через defineProperty с дескриптором свойств данных
Object.defineProperty(o, "a", {value : 37,
writable : true,
enumerable : true,
configurable : true});
// Свойство 'a' существует в объекте и его значение равно 37
// Пример: свойство объекта добавлено через defineProperty с дескриптором доступа к свойствам(get/set)
var bValue;
Object.defineProperty(o, "b", {get : function(){ return bValue; },
set : function(newValue){ bValue = newValue; },
enumerable : true,
configurable : true});
o.b = 38;
// Свойство 'b' существует в объекте и его значение равно 38
// Значение o.b теперь идентично bValue, пока o.b не переопределено
// Можно попробовать использовать оба подхода вместе:
Object.defineProperty(o, "conflict", { value: 0x9f91102,
get: function() { return 0xdeadbeef; } });
// Бросает исключение TypeError:
// value appears only in data descriptors, get appears only in accessor descriptors
Изменение свойства
Если свойство уже существует, Object.defineProperty () попытается изменить свойство в соответствии со значениями в дескрипторе и текущей конфигурацией объекта. Если у старого дескриптора атрибут configurable был равен false (свойство, как говорят, "не настраивается" или "не конфигурируется"), то никакие атрибуты, кроме writable, не могут быть изменены. В этом случае, также невозможно переключаться между данными и средствами доступа(аксесорами) свойств.
Если свойство неконфигурируемое(non-configurable), его аттрибут writable может быть изменен только на false.
Объект TypeError будет брошен при попытке изменить аттрибуты неконфигурируемого(non-configurable) свойства (кроме аттрибута writable) , если текущее и новое значения не равны.
Записываемые атрибуты
Когда аттрибут свойства writable равен false,свойство называется "незаписываемым"("non-writable"). Оно не может быть переназначено.
Пример
var o = {}; // Создает новый объект
Object.defineProperty(o, "a", { value : 37,
writable : false });
console.log(o.a); // Выводит 37
o.a = 25; // Нет ошибки (ошибка появляется в strict mode, даже если значение будет таким же)
console.log(o.a); // Выводит 37. Присваивание не сработало.
Как показано на примере выше, попытка записать новое значение в незаписываемое свойство не изменяет его значения, но также и не бросает ошибку.
Перечислимые атрибуты
Аттрибут свойства enumerable определяет, будет ли свойство появляется в циклах for...in и Object.keys().
Пример
var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // Перечисляемое по умолчанию равно 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
Аттрибут configurable
Аттрибут configurable одновременно контролирует две вещи: может ли свойство объекта быть удалено и могут ли его аттрибуты(все, кроме writable) быть изменены.
Пример
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
Если бы аттрибут configurable объекта o.a был бы равен true, никаких ошибок не появилось бы и свойство было бы удалено.
Добавление свойств и значений по умолчанию
Важно понимать, как значения по умолчанию назначаются свойствам. Часто, существует разница между назначением значения через точку и использованием Object.defineProperty(), как показано на примере ниже:
var o = {};
o.a = 1;
// is equivalent to :
Object.defineProperty(o, "a", {value : 1,
writable : true,
configurable : true,
enumerable : true});
// On the other hand,
Object.defineProperty(o, "a", {value : 1});
// is equivalent to :
Object.defineProperty(o, "a", {value : 1,
writable : false,
configurable : false,
enumerable : false});
Кросс-браузерность
Переопределение свойства length объекта Array
It is possible to redefine the length property of arrays, subject to the usual redefinition restrictions. (The length property is initially non-configurable, non-enumerable, and writable. Thus on an unaltered array it is possible to change the lengthproperty's value, or to make it non-writable. It is not allowed to change its enumerability or configurability, or if it is non-writable to change its value or writability.) However, not all browsers permit this redefinition.
Firefox 4 through 22 will throw a TypeError on any attempt whatsoever (whether permitted or not) to redefine the length property of an array.
Versions of Chrome which implement Object.defineProperty() in some circumstances ignore a length value different from the array's current length property. In some circumstances changing writability seems to silently not work (and not throw an exception). Also, relatedly, some array-mutating methods like Array.prototype.push don't respect a non-writable length.
Versions of Safari which implement Object.defineProperty() ignore a length value different from the array's current lengthproperty, and attempts to change writability execute without error but do not actually change the property's writability.
Only Internet Explorer 9 and later, and Firefox 23 and later, appear to fully and correctly implement redefinition of the lengthproperty of arrays. For now, don't rely on redefining the length property of an array to either work, or to work in a particular manner. And even when you can rely on it, there's really no good reason to do so.
Особый случай Internet Explorer 8
Реализация метода Object.defineProperty() в Internet Explorer 8 предусматривает, что он может быть использован только для объектов DOM. Нужно также отметить несколько моментов:
- Попытка использовать
Object.defineProperty()для нативных объектов приведет к ошибке. - Аттрибуты свойств должны быть равны определенным значениям.
true, true, trueдля дескриптора данных иtrueдля configurable,falseдля enumerable для десприптора доступа к данным(аксесора).(?) Любая попытка присвоить другие значения аттрибутам приведет к ошибке. - Для того, чтобы изменить конфигурацию свойства, нужно вначале его удалить. Если свойство не было удалено, то оно остается в том же виде, что до попытки изменить его.
Совместимость с браузерами
| Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|
| Basic support | 4 (2) | 5 (previous versions untested) | 9 (8, but only on DOM objects and with some non-standard behaviors. See above.) | 11.60 | 5.1 (5, but not on DOM objects) |
| Feature | Firefox Mobile (Gecko) | Android | IE Mobile | Opera Mobile | Safari Mobile |
|---|---|---|---|---|---|
| Basic support | 4.0 (2) | (Yes) | ? | 11.50 | (Yes) |
Based on Kangax's compat tables.