JavaScript метод defineProperties()

JavaScript Object

Определение и применение

JavaScript метод defineProperties() позволяет определить новые или изменить существующие свойства объекта, описывая их дескрипторами.


С использованием метода defineProperties() допускается определять или изменять только одно существующее свойство объекта, описывая его дескриптором, но Вы также можете воспользоваться методом defineProperty(), предназначенного как раз для этой задачи.


Дескрипторы свойств

Дескрипторы свойств – это обычные объекты JavaScript, которые описывают атрибуты и значение свойства, они подразделяются на два основных типа:

Отличительная особенность дескриптора со свойством данных заключается в том, что он устанавливает значение (ключ value), которое ассоциируется со свойством. В качестве значения может использоваться лю­бое валидное в JavaScript зна­че­ние (число, строка, объект и тому подобное). Значение по умолчанию undefined.

Второй ключ, который используется в дескрипторе со свойством данных это ключ writable, он позволяет установить значение свойства объекта как доступным для записи, так и недоступным для записи (допускается ли изменение свойства объекта с помощью оператора присваивания, или нет). Логическое значение true определяет, что свойство доступно для записи, а значение false, что свойство недоступно для записи. При создании свойства значение атрибута по умолчанию будет установлено false, если не указано обратное.


Обратите внимание, что ключи, используемые внутри дескрипторов свойств часто в информационных и литературных источниках называют атрибутами.


Следующие два ключа (атрибута), используются как в дескрипторах свойств с данными, так и в дескрипторах свойств с методами доступа:

Дескриптор свойства с данными в котором указаны все атрибуты имеет следующий вид:

{
    value: "validValue",  // лю­бое валидное в JavaScript зна­че­ние (по умолчанию undefined)
    writable: true,            // логическое значение (по умолчанию false)
    enumerable: true,    // логическое значение (по умолчаниюfalse)
    configurable: true,   // логическое значение (по умолчанию false)
}

Отличительная особенность дескриптора свойств с методами доступа заключается в том, что он имеет метод чтения (функция "геттер" - атрибут get) и/или метод записи (функция "сеттер" - атрибут set):

Дескриптор свойства с методами доступа в котором указаны все атрибуты имеет следующий вид:

{
    get: function() {},       // функция геттер (по умолчанию undefined)
    set: function() {},       // функция cеттер (по умолчанию undefined)
    enumerable: true,    // логическое значение (по умолчанию false)
    configurable: true,   // логическое значение (по умолчанию false)
}

Обратите внимание на важный момент дескриптор должен содержать либо свойство с данными, либо свойство с методами доступа, он не может соответствовать обоим рассмотренным нами типам, иначе это приведет к ошибке (исключение TypeError).

Поддержка браузерами

МетодChrome
Chrome
Firefox
Firefox
Opera
Opera
Safari
Safari
Internet Explorer
IExplorer
Microsoft Edge
Edge
defineProperties()5.04.011.65.1*9.0*Да

JavaScript синтаксис:

Object.defineProperties( obj, props );

Версия JavaScript

ECMAScript 5.1 (реализовано в JavaScript 1.8.5)

Значения параметров

ПараметрОписание
objЦелевой объект в котором определяется свойство. Обязательное значение.
propsОбъект, чьи собственные перечисляемые свойства представляют собой новые или изменяемые существующие свойства целевого объекта, а в качестве значений этих свойств используются дескрипторы. Допускается определять или изменять только одно свойство.

В дескрипторе допускается использование следующих атрибутов:

Атрибуты, которые используется только с дескрипторами свойств с данными:

  • Атрибут value - устанавливает значение, которое ассоциируется со свойством. В качестве значения может использоваться лю­бое валидное в JavaScript зна­че­ние (число, строка, объект и тому подобное). Значение по умолчанию undefined.
  • Атрибут writable - позволяет установить значение свойства объекта как доступным для записи, так и недоступным для записи (допускается ли изменение свойства объекта с помощью оператора присваивания, или нет). Логическое значение true определяет, что свойство доступно для записи, а значение false, что свойство недоступно для записи. Значение по умолчанию false.

Атрибуты, которые используется только с дескрипторами свойств с методами доступа:

  • Атрибут get - функция, которая позволяет получить значение для свойства (при обращении к свойству эта функция вызывается без параметров). Такие функции называют "геттером", возвращаемое значение такой функции используется в качестве значения свойства. Если функция не задана, то значением по умолчанию будет значение undefined.
  • Атрибут set - функция, которая позволяет установить значение для свойства. Такие функции называют "сеттером", когда свойство назначено, эта функция вызывается с одним параметром - значение, которое будет присвоено свойству. Если функция не задана, то значением по умолчанию будет значение undefined.

Атрибуты, которые допускается использовать во всех дескрипторах:

  • Атрибут enumerable - в качестве значения атрибута указывается логическое значение, которое определяет отображается ли данное свойство при перечислении свойств объекта в котором оно содержится в цикле for..in, или при вызове метода Object.keys(), который возвращает массив, содержащий имена всех собственных перечислимых (неунаследованных) свойств указанного объекта. Если указано true, то свойство перечисляется, если false, то нет. Значение по умолчанию false.
  • Атрибут configurable - в качестве значения атрибута указывается логическое значение, которое определяет допускается ли удаление этого свойства из содержащего его объекта, а также допускается ли в дальнейшем изменение дескриптора этого свойства в будущем. Если указано true, то свойство будет доступно к удалению и изменению его дескриптора, если false, то нет. Значение по умолчанию false.

Исключения

Тип исключенияОписание
TypeErrorВозникает в том случае, если:
  • Значение параметра, определяющее целевой объект в котором определяется свойство не является объектом.
  • Объект не является расширяемым (запрещено добавление к объекту новых свойств), а указанное имя свойства не существует.
  • У дескрипотра одновременно указаны атрибуты дескриптора свойств с методами доступа и дескриптора свойств с данными. Например, атрибут value или writable, и get или set.
  • Дескриптор имеет атрибут get или set, который не является функцией или значением undefined.
  • Указанное имя свойства уже существует, существующее свойство имеет атрибут configurable со значением false и дескриптор содержит один или несколько атрибутов, которые отличаются от атрибутов в существующем свойстве. Однако, если существующее свойство имеет атрибут configurable со значением false и атрибут writable со значением true, атрибут value или writable может быть изменен.

Пример использования

Добавление свойства данных

В этом примере мы рассмотрим как с помощью метода defineProperty() добавить в объект новые свойства данных:

// инициализируем переменную, содержащую объект
let obj = {};

// добавляем в объект новое свойство данных
Object.defineProperties( obj, {
    "newProperty": {
        value: "myValue",  // устанавливаем значение свойства 
        writable: true,    // устанавливаем, что свойство доступно для записи 
        enumerable: true,  // устанавливаем, что свойство перечислимо 
        configurable: true // устанавливаем, что удаление свойства допускается, а также допускается изменение дескриптора этого свойства 
    },
    "newProperty2": {
        value: "myValue2", // устанавливаем значение свойства 
        writable: true,    // устанавливаем, что свойство доступно для записи 
    },
});

console.log( obj.newProperty );  // свойство содержит значение "myValue"
console.log( obj.newProperty2 ); // свойство содержит значение "myValue2"

obj.newProperty = "newValue";     // изменяем значение свойства в объекте
obj.newProperty2 = "newValue2";   // изменяем значение свойства в объекте

console.log( obj.newProperty );  // свойство содержит значение "newValue"
console.log( obj.newProperty2 ); // свойство содержит значение "newValue2"

Изменение свойства данных

В этом примере мы рассмотрим как с помощью метода defineProperty() изменить существующие свойство объекта, описывая их дескрипторами:

let obj = {  // инициализируем переменную, содержащую объект
    myProperty: 1000,
    myProperty2: 2000
};

obj.myProperty = 100;  // изменяем значение свойства в объекте
obj.myProperty2 = 200; // изменяем значение свойства в объекте
console.log( obj.myProperty );  // свойство содержит значение 100
console.log( obj.myProperty2 ); // свойство содержит значение 200

// изменяем в объекте существующие свойства
Object.defineProperties( obj, {
    "myProperty": {
        value: 1000,  // устанавливаем значение свойства 
        writable: false // устанавливаем, что свойство недоступно для записи 
    },
    "myProperty2": {
        value: 2000, // устанавливаем значение свойства 
        writable: false // устанавливаем, что свойство недоступно для записи 
    },
});

console.log( obj.myProperty );  // свойство содержит значение 1000
console.log( obj.myProperty2 ); // свойство содержит значение 2000

// изменяем значение свойства в объекте
obj.myProperty = 100;  // в режиме "use strict" вызывается исключение TypeError
obj.myProperty2 = 200; // в режиме "use strict" вызывается исключение TypeError
console.log( obj.myProperty );  // свойство содержит значение 1000
console.log( obj.myProperty2 ); // свойство содержит значение 2000

В этом примере с использованием метода defineProperty() мы изменили существующие свойства объекта, описывая их дескрипторами в которых указали, что свойства недоступны для записи (не допускается изменение свойства объекта с помощью оператора присваивания). Благодаря этому мы предотвратили изменение свойств в объектах.

Обратите внимание, что в строгом режиме ("use strict"), установленного для скрипта вызывается исключение TypeError при попытке изменить свойство недоступное для записи, в обычном режиме ошибки нет. Строгий режим был введен в ECMAScript 5, он позволяет находить некоторые ошибки быстрее и запрещает некоторые опасные особенности языка. Этот режим не поддерживают старые браузеры, например, IE 9 и ниже, в этих браузерах код будет выполняться в обычном режиме (игнорируется директива "use strict").

Добавление свойств метода доступа

В этом примере мы рассмотрим как с помощью метода defineProperty() добавить в объект новое свойство с методами доступа:

// инициализируем переменную, содержащую объект
let obj = {};

// добавляем в объект новое свойство с методами доступа
Object.defineProperties( obj, {
    "myProp": {
        get: function(){ // устанавливаем функцию, которая позволяет получить значение для свойства
            console.log( "using getter (myProp)" );
            return this.myPropVal // возвращаем значение свойства
        },
        set: function( newValue ){ // устанавливаем функцию, которая позволяет установить новое значение для свойства
            console.log( "using setter (myProp)" );
            this.myPropVal = newValue; // устанавливаем значение свойства
        },
        enumerable: true, // устанавливаем, что свойство перечислимо 
        configurable: true // устанавливаем, что удаление свойства допускается, а также допускается изменение дескриптора этого свойства
    },
    "myProp2": {
        get: function(){ // устанавливаем функцию, которая позволяет получить значение для свойства
            console.log( "using getter (myProp2)" );
            return this.myPropVal // возвращаем значение свойства
        },
        set: function( newValue ){ // устанавливаем функцию, которая позволяет установить новое значение для свойства
            console.log( "using setter (myProp2)" );
            this.myPropVal = newValue; // устанавливаем значение свойства
        },
        enumerable: true, // устанавливаем, что свойство перечислимо 
        configurable: true // устанавливаем, что удаление свойства допускается, а также допускается изменение дескриптора этого свойства
    },
});

console.log( obj.myProp ); 
// using getter (myProp)
// undefined

console.log( obj.myProp2 ); 
// using getter (myProp)
// undefined

obj.myProp = 100;

// using setter (myProp)
// 100

obj.myProp2 = 200;

// using setter (myProp2)
// 200

Обратите внимание на важный момент, функции "геттер" и "сеттер" должны ссылаться внутри себя на значение свойства this.любоеИмя, а не на имя своего свойства, иначе это приведет к ошибкам, бесконечным ошибкам.

Сокрытие свойства объекта от перечисления

В следующем примере мы рассмотрим как с помощью метода defineProperty() скрыть свойство объекта при перечислении в цикле for..in и при вызове метода Object.keys():

let obj = {a: 1, b: 2, c: 3, d: 4};  // инициализируем переменную, содержащую объект

for ( let key in obj ) {  // выводим в консоль значения свойств объекта в цикле
    console.log( key );
};
// a
// b
// c
// d

// выводим имена всех собственных перечислимых (неунаследованных) свойств указанного объекта
Object.keys( obj );  // возвращаемое значение ["a", "b", "c", "d"]

// изменяем в объекте существующие свойства
Object.defineProperties( obj, {
    "a": {
        enumerable: false // устанавливаем, что свойство не перечислимо
    },
    "c": {
        enumerable: false // устанавливаем, что свойство не перечислимо
    },
});
for ( let key in obj ) {  // выводим в консоль значения свойств объекта в цикле
    console.log( key );
};
// b
// d

// выводим имена всех собственных перечислимых (неунаследованных) свойств указанного объекта
Object.keys( obj );  // возвращаемое значение ["b", "d"]
JavaScript Object