Object.defineProperty()

Переклад не закінчено. Будь ласка, допоможіть перекласти цю статтю з англійської.

Object.defineProperty() метод визначає нову властивість прямо на об'єкті, або модифікує існуючю властивість на об'єкті та повертає об'єкт.

Ситаксис

Object.defineProperty(obj, prop, descriptor)

Параметри

obj
Об'єкт на якому визначаєтся властивість.
prop
Ім'я властивості для визначення чи модифікації.
descriptor
Дескриптор для властивості що модифікуєтся чи визначаєтся.

Повертає значення

Об'єкт що був переданий в функцію.

Опис

Цей метод дозволяє чітке додавання або модифікацію властивостей об'єкту. Звичайне додавання властивостей через призначення створює властивості які з'являются під час переліку властивостей (for...in у циклі або Object.keys method), їх значення можуть бути змінені, та можуть бути видалені deleted. Цей метод дозволяє змінити ці додаткові деталі зі стану за замовчуванням(default). За замовчуванням значення додані з використанням Object.defineProperty() незмінні(not writable).

Дескриптори властивостей в об'єктах бувають двох основних видів: data descriptors та accessor descriptors. Data descriptor це властивість що має значення(value), що може або не може бути перезаписана(writable). Accessor descriptor це властивість зображена парою функцій getter-setter. Дескриптор має бути одним з двох видів, і не може бути обома.

Обидва data та accessor дескриптори є об'єктами. Вони поділяють наступні необхідні опції:

configurable
true тільки якщо тип цієї властивості дескриптора може бути змінений та якщо властивість може бути видалена з цього об'єкту.
За замовчуванням false.
enumerable
true тільки якщо ця властивість з'являєтся під час переліку властивостей на цьому об'єкті..
За замовчуванням false.

Data descriptor також має наступні необов'язкові опції:

value
Значення пов'язане з властивістю. Може бути дійсним(valid) значенням у JavaScript (number, object, function, etc).
За замовчуванням undefined.
writable
true тільки якщо значення пов'язане з властивістю може бути змінено за допомогою assignment operator.
За замовчуванням false.

Accessor descriptor також має наступні необов'язкові опції:

get
Функція що служить геттером для властивості, або undefined якщо немає геттера. Те що повертає функція буде використано як значення властивості(property).
За замовчуванням undefined.
set
Функція що служить як сеттер для властивості, або undefined якщо сеттеру немає. Функція отримає в якості єдиного аргументу нове значення що буде присвоєне властивості.
За замовчуванням undefined.

Пам'ятайте що ці опції не обов'язково власні властивості дескриптору,  властивості успадковані з ланцюга прототипів також будуть вважатись. Щоб впевнитись що замовчування збережутся, ви можете заморозити Object.prototype наперед, явно вказуючи всі опції, або вказати null як __proto__ властивості.

// використовуючи __proto__
var obj = {};
Object.defineProperty(obj, 'key', {
  __proto__: null, // no inherited properties(без успадкованих властивостей)
  value: 'static'  // not enumerable
                   // not configurable
                   // not writable
                   // as defaults
});

// явно
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;
}
// ... and ...
Object.defineProperty(obj, 'key', withValue('static'));

// якщо замороження доступне, запобігає додаванню або 
// видаленню властивостей з прототипу об'єкта
// (value, get, set, enumerable, writable, configurable)  
(Object.freeze || Object)(Object.prototype);

Приклади

Якщо ви хочете побачити використання методу Object.defineProperty разом з двійковими прапорцями, дивіться додаткові приклади.

Створення властивості

Коли зазначена властивість не існує в об'єкті, тоді Object.defineProperty() створює нову властивість з опису. Поля можуть бути пропущені з дескриптору, тоді для цих полів будуть поставлені значення за замовчуванням. Всі Булевозначні поля за замовчуванням false. Поля value, get, та set за замовчуванням undefined. Властивість визначена без get/set/value/writable називаєтся загальною(generic) та типізована як data descriptor.

var o = {}; // Creates a new object

// Приклад властивості об'єкту доданої за допомогою defineProperty з властивістю data descriptor
Object.defineProperty(o, 'a', {
  value: 37,
  writable: true,
  enumerable: true,
  configurable: true
});
// 'а' властивість існує в об'єкті o та її значення 37

// Приклад властивості об'єкту доданої за допомогою defineProperty з властивістю property descriptor
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: значення буває тільки в data descriptors, get буває тільки в accessor descriptors
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors

Зміна властивості

Коли властивість вже існує, Object.defineProperty()намагається модифікувати властивість згідно значень у дескрипторі властивості та поточної конфігурації об'єкта. Якщо старий дескриптор мав свій атрибут 'configurable ' в стані false (властивість як сказано не конфігурується(non-configurable), тоді жоден атрибут окрім writable не може бути змінений. В цьому випадку неможливо переходити з data типу в accessor тип та навпаки.

Якщо властивість non-configurable, її writable атрибут може бути змінено тільки на false.

Помилка TypeError з'являється коли мають місце спроби змінити атрибути non-configurable властивості(також writable атрибут) окрім випадків коли поточне значення теж саме що й нове.

Writable attribute

Коли атрибут writable встановлений у false, властивість як сказано 'non-writable'. Вона не може бути перепризначена.

var o = {}; // Creates a new object

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

console.log(o.a); // logs 37
o.a = 25; // No error thrown (it would throw in strict mode, even if the value had been the same)
console.log(o.a); // logs 37. The assignment didn't work.

As seen in the example, trying to write into the non-writable property doesn't change it but doesn't throw an error either.

Enumerable attribute

The enumerable property attribute defines whether the property shows up in a for...in loop and Object.keys() or not.

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; // enumerable defaults to true when creating a property by setting it

for (var i in o) {
  console.log(i);
}
// logs 'a' and 'd' (in undefined order)

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

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

Configurable attribute

The configurable attribute controls at the same time whether the property can be deleted from the object and whether its attributes (other than writable) can be changed.

var o = {};
Object.defineProperty(o, 'a', {
  get: function() { return 1; },
  configurable: false
});

Object.defineProperty(o, 'a', { configurable: true }); // throws a TypeError
Object.defineProperty(o, 'a', { enumerable: true }); // throws a TypeError
Object.defineProperty(o, 'a', { set: function() {} }); // throws a TypeError (set was undefined previously)
Object.defineProperty(o, 'a', { get: function() { return 1; } }); // throws a TypeError (even though the new get does exactly the same thing)
Object.defineProperty(o, 'a', { value: 12 }); // throws a TypeError

console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1

If the configurable attribute of o.a had been true, none of the errors would be thrown and the property would be deleted at the end.

Adding properties and default values

It's important to consider the way default values of attributes are applied. There is often a difference between simply using dot notation to assign a value and using Object.defineProperty(), as shown in the example below.

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
});

Custom Setters and Getters

Example below shows how to implement a self-archiving object. When temperature property is set, the archive array gets a log entry.

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 }]

or

var pattern = {
    get: function () {
        return 'I always return this string, whatever you have assigned';
    },
    set: function () {
        this.myname = 'this is my name string';
    }
};


function TestDefineSetAndGet() {
    Object.defineProperty(this, 'myproperty', pattern);
}


var instance = new TestDefineSetAndGet();
instance.myproperty = 'test';
console.log(instance.myproperty); // I always return this string, whatever you have assigned

console.log(instance.myname); // this is my name string

Specifications

Specification Status Comment
ECMAScript 5.1 (ECMA-262)
The definition of 'Object.defineProperty' in that specification.
Standard Initial definition. Implemented in JavaScript 1.8.5.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Object.defineProperty' in that specification.
Standard  
ECMAScript 2017 Draft (ECMA-262)
The definition of 'Object.defineProperty' in that specification.
Draft  

Browser compatibility

Feature Firefox (Gecko) Chrome Internet Explorer Opera Safari
Basic support 4.0 (2) 5 9 [1] 11.60 5.1 [2]
Feature Firefox Mobile (Gecko) Android IE Mobile Opera Mobile Safari Mobile
Basic support 4.0 (2) (Yes) 9 11.5 (Yes)

[1] In Internet Explorer 8 only on DOM objects and with some non-standard behaviors.

[2] Also supported in Safari 5, but not on DOM objects.

Compatibility notes

Redefining the length property of an Array object

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 length property'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 length property, 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 length property 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 specific notes

Internet Explorer 8 implemented a Object.defineProperty() method that could only be used on DOM objects. A few things need to be noted:

  • Trying to use Object.defineProperty() on native objects throws an error.
  • Property attributes must be set to some values. The configurable, enumerable and writable attributes should all be set to true for data descriptor and true for configurable, false for enumerable for accessor descriptor.(?) Any attempt to provide other value(?) will result in an error being thrown.
  • Reconfiguring a property requires first deleting the property. If the property isn't deleted, it stays as it was before the reconfiguration attempt.

See also

Мітки документа й учасники

 Зробили внесок у цю сторінку: romkor, MontyFelix
 Востаннє оновлена: romkor,