JavaScript метод Object.defineProperty()
Определение и применение
JavaScript метод defineProperty() позволяет определить новое или изменить существующее свойство объекта,
описывая его дескрипторами.
Дескрипторы свойств
Дескрипторы свойств – это обычные объекты JavaScript, которые описывают атрибуты и
значение свойства, они подразделяются на два основных типа:
- дескрипторы свойств с данными.
- дескрипторы свойств с методами доступа.
Отличительная особенность дескриптора со свойством данных заключается в том, что он устанавливает
значение (ключ value), которое ассоциируется со свойством. В качестве значения может
использоваться любое валидное в JavaScript значение (число, строка, объект и тому подобное). Значение по
умолчанию undefined.
Второй ключ, который используется в дескрипторе со свойством данных это
ключ writable, он позволяет установить значение свойства объекта как доступным для
записи, так и недоступным для записи (допускается ли изменение свойства объекта с помощью оператора присваивания, или
нет). Логическое значение true определяет, что свойство доступно для записи, а значение
false, что свойство недоступно для записи. При создании свойства значение атрибута по умолчанию будет
установлено false, если не указано обратное.
Следующие два ключа (атрибута), используются как в дескрипторах свойств с данными, так и в дескрипторах свойств с методами доступа:
-
Атрибут
enumerable- в качестве значения атрибута указывается логическое значение, которое определяет отображается ли данное свойство при перечислении свойств объекта в котором оно содержится в циклеfor...in, или при вызове методаObject.keys(), который возвращает массив, содержащий имена всех собственных перечислимых (неунаследованных) свойств указанного объекта. Если указаноtrue, то свойство перечисляется, еслиfalse, то нет. При создании свойства значение атрибута по умолчанию будет установленоfalse, если не указано обратное. -
Атрибут
configurable- в качестве значения атрибута указывается логическое значение, которое определяет допускается ли удаление этого свойства из содержащего его объекта, а также допускается ли в дальнейшем изменение дескриптора этого свойства в будущем. Если указаноtrue, то свойство будет доступно к удалению и изменению его дескриптора, еслиfalse, то нет. При создании свойства значение атрибута по умолчанию будет установленоfalse, если не указано обратное.
Дескриптор свойства с данными в котором указаны все атрибуты имеет следующий вид:
{
value: "validValue", // любое валидное в JavaScript значение (по умолчанию undefined)
writable: true, // логическое значение (по умолчанию false)
enumerable: true, // логическое значение (по умолчанию false)
configurable: true, // логическое значение (по умолчанию false)
}
Отличительная особенность дескриптора свойств с методами доступа заключается в том, что он имеет
метод чтения (функция "геттер" - атрибут get) и/или метод записи (функция
"сеттер" - атрибут set):
-
Атрибут
get- функция, которая позволяет получить значение для свойства (при обращении к свойству эта функция вызывается без параметров). Такие функции называют "геттером", возвращаемое значение такой функции используется в качестве значения свойства. Если функция не задана, то значением по умолчанию будет значениеundefined. -
Атрибут
set- функция, которая позволяет установить значение для свойства. Такие функции называют "сеттером", когда свойство назначено, эта функция вызывается с одним параметром - значение, которое будет присвоено свойству. Если функция не задана, то значением по умолчанию будет значениеundefined.
Дескриптор свойства с методами доступа в котором указаны все атрибуты имеет следующий вид:
{
get: function() {}, // функция геттер (по умолчанию undefined)
set: function() {}, // функция сеттер (по умолчанию undefined)
enumerable: true, // логическое значение (по умолчанию false)
configurable: true, // логическое значение (по умолчанию false)
}
Дескриптор не может одновременно содержать value или writable и get или
set: это вызывает TypeError. Пустой дескриптор и дескриптор только с
enumerable/configurable допустимы. При создании нового свойства они создают свойство данных,
при изменении сохраняют прежний тип свойства.
configurable: false запрещает удаление и большинство изменений дескриптора. У свойства данных с
writable: true ещё можно менять значение и установить writable: false; обратный переход
запрещён. Повторная установка того же значения атрибута допустима. Значения по умолчанию применяются при создании
свойства; пропущенные атрибуты существующего свойства сохраняются. Геттер и сеттер могут хранить данные в замыкании:
использовать другое свойство через this необязательно.
Поддержка браузерами
Спецификация: ecma262
Поддерживается во всех основных браузерах с 2015 года.
| Возможность | Компьютеры | Мобильные | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
defineProperty | 5 | 12 | 4 | 11.6 | 5.1 Версия 5.1 выпущена в 2011 г. Примечание MDN (англ.) Also supported in Safari 5, but not on DOM objects. | 18 | 4 | 12 | 6 Версия 6 выпущена в 2012 г. Примечание MDN (англ.) Also supported in Safari for iOS 4.2, but not on DOM objects. | 1.0 | 4.4 | 6 Версия 6 выпущена в 2012 г. Примечание MDN (англ.) Also supported in Safari for iOS 4.2, but not on DOM objects. |
поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31
JavaScript синтаксис
Object.defineProperty( obj, prop, descriptor );
Спецификация
ECMAScript: Object.defineProperty()
Значения параметров
| Параметр | Описание |
|---|---|
obj |
Целевой объект в котором определяется свойство. Обязательное значение. |
prop |
Имя свойства, которое необходимо определить или изменить. |
descriptor |
Дескриптор определяемого или изменяемого свойства. В дескрипторе допускается использование следующих
атрибутов:
Атрибуты, которые используется только с дескрипторами свойств с данными:
Атрибуты, которые используется только с дескрипторами свойств с методами доступа:
Атрибуты, которые допускается использовать во всех дескрипторах:
|
Исключения
| Тип исключения | Описание |
|---|---|
TypeError |
Возникает в том случае, если:
|
Метод возвращает тот же целевой объект. Поля дескриптора читаются с учётом его прототипа; для get и
set допустимы функции или undefined.
Примеры, попробуйте сами
Добавление свойства данных
В этом примере мы рассмотрим как с помощью метода defineProperty() добавить в объект новое свойство
данных:
В примере «Создание свойства данных» показан результат вызова метода.
Создание свойства данных
Измените свойства исходного объекта и сравните результат до и после вызова метода.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Создание свойства данных</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
// инициализируем переменную, содержащую объект
let obj = {}
// добавляем в объект новое свойство данных
Object.defineProperty(obj, 'newProperty', {
value: 'myValue', // устанавливаем значение свойства
writable: true, // устанавливаем, что свойство доступно для записи
enumerable: true, // устанавливаем, что свойство перечислимо
configurable: true, // устанавливаем, что удаление свойства допускается, а также допускается изменение дескриптора этого свойства
})
console.log(obj.newProperty) // свойство содержит значение "myValue"
obj.newProperty = 'newValue' // изменяем значение свойства в объекте
console.log(obj.newProperty) // свойство содержит значение "newValue"
Изменение свойства данных
В этом примере мы рассмотрим как с помощью метода defineProperty() изменить существующее свойство
объекта, описывая его дескриптором:
В примере «Запрет записи» показан результат вызова метода.
Запрет записи
Измените свойства исходного объекта и сравните результат до и после вызова метода.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Запрет записи</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
// инициализируем переменную, содержащую объект
let obj = {
myProperty: 1000,
}
obj.myProperty = 100 // изменяем значение свойства в объекте
console.log(obj.myProperty) // свойство содержит значение 100
// изменяем в объекте существующее свойство
Object.defineProperty(obj, 'myProperty', {
value: 1000, // устанавливаем значение свойства
writable: false, // устанавливаем, что свойство недоступно для записи
})
console.log(obj.myProperty) // свойство содержит значение 1000
// изменяем значение свойства в объекте
obj.myProperty = 100 // в режиме "use strict" вызывается исключение TypeError
console.log(obj.myProperty) // свойство содержит значение 1000
В этом примере с использованием метода defineProperty() мы изменили существующее свойство объекта,
описывая его дескриптором в котором указали, что свойство недоступно для записи (не допускается изменение свойства
объекта с помощью оператора присваивания). Благодаря этому мы предотвратили изменение свойства в объекте.
Обратите внимание, что в строгом режиме ("use strict"), установленного для скрипта вызывается исключение
TypeError при попытке изменить свойство недоступное для записи, в обычном режиме ошибки нет. В модулях
строгий режим действует автоматически.
Добавление свойства метода доступа
В этом примере мы рассмотрим как с помощью метода defineProperty() добавить в объект новое свойство с
методами доступа:
В примере «Свойство с геттером и сеттером» показан результат вызова метода.
Свойство с геттером и сеттером
Измените свойства исходного объекта и сравните результат до и после вызова метода.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Свойство с геттером и сеттером</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
// инициализируем переменную, содержащую объект
let obj = {}
// добавляем в объект новое свойство с методами доступа
Object.defineProperty(obj, 'test', {
get: function () {
// устанавливаем функцию, которая позволяет получить значение для свойства
console.log('using getter')
return this.testValue // возвращаем значение свойства
},
set: function (newValue) {
// устанавливаем функцию, которая позволяет установить новое значение для свойства
console.log('using setter')
this.testValue = newValue // устанавливаем значение свойства
},
enumerable: true, // устанавливаем, что свойство перечислимо
configurable: true, // устанавливаем, что удаление свойства допускается, а также допускается изменение дескриптора этого свойства
})
console.log(obj.test)
// using getter
// undefined
obj.test = 5
// using setter
// 5
Геттер и сеттер не должны обращаться к тому же свойству через this: это вызывает рекурсию. Храните
значение в другом свойстве или в замыкании.
Сокрытие свойства объекта от перечисления
В следующем примере мы рассмотрим как с помощью метода defineProperty() скрыть свойство объекта при
перечислении в цикле for...in и при вызове метода Object.keys():
В примере «Перечислимость свойства» показан результат вызова метода.
Перечислимость свойства
Измените свойства исходного объекта и сравните результат до и после вызова метода.
index.html
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Перечислимость свойства</title>
</head>
<body>
<p>Результат выполнения выводится в консоль.</p>
</body>
</html>
main.js
// инициализируем переменную, содержащую объект
let obj = { a: 1, b: 2, c: 3 }
// выводим в консоль значения свойств объекта в цикле
for (let key in obj) {
console.log(key)
}
// a
// b
// c
// выводим имена всех собственных перечислимых (неунаследованных) свойств указанного объекта
console.log(Object.keys(obj)) // возвращаемое значение ["a", "b", "c"]
// изменяем в объекте существующее свойство
Object.defineProperty(obj, 'b', {
enumerable: false, // устанавливаем, что свойство не перечислимо
})
// выводим в консоль значения свойств объекта в цикле
for (let key in obj) {
console.log(key)
}
// a
// c
// выводим имена всех собственных перечислимых (неунаследованных) свойств указанного объекта
console.log(Object.keys(obj)) // возвращаемое значение ["a", "c"]