В этой статье мы изучим доступные для записи, перечисляемые и настраиваемые атрибуты свойства, а также использование методов получения и установки.

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

Каждое свойство объекта - это больше, чем просто пара имени и значения. У каждого свойства есть дескриптор свойства, который помогает увидеть все атрибуты этого свойства.

var bike = {
  name: 'SuperSport', 
  maker: 'Ducati', 
  engine: '937cc'
};
console.log(Object.getOwnPropertyDescriptor(bike, 'engine'));
//Output will be below object
{
  configurable: true,
  enumerable: true,
  value: "937cc",
  writable: true
}

В приведенном выше фрагменте кода мы регистрируем все атрибуты свойства engine объекта bike с помощью метода getOwnPropertyDescriptor(). Итак, мы можем видеть это дополнение к атрибуту value, у него есть еще три атрибута (configurable, enumerable и Writable ). Все это true по умолчанию.

Давайте разберемся со всеми атрибутами по порядку.

записываемый атрибут

Атрибут Writable определяет, можно ли изменить значение, связанное со свойством, от его начального значения. мы можем изменить атрибуты свойства, используя метод Object.defineProperty(), как показано ниже

var bike = {
  name: 'SuperSport', 
  maker:'Ducati', 
  engine:'937cc'
};
Object.defineProperty(bike, 'engine', {writable: false});
bike.engine = '2000cc';
// Throws error in strict mode
// Uncaught TypeError: Cannot assign to read only property
console.log(bike.engine);
// Output will be 937cc in non-strict mode

В приведенном выше фрагменте кода мы видим, что атрибут writable свойства engine, измененный на false, означает, что мы сделали его недоступным для записи. Затем, если мы попытаемся изменить значение свойства engine, будет выдана ошибка с сообщением «TypeError: Невозможно назначить свойство только для чтения». Но ошибка отображается только в том случае, если код выполняется в строгом режиме, в противном случае происходит сбой без уведомления и значение engine property не обновляется. Таким образом, bike.engine по-прежнему печатает свое исходное значение в случае нестрогого режима.

Давайте рассмотрим еще один пример, чтобы понять интересную часть записываемого свойства. Если свойство, недоступное для записи, само по себе является объектом, мы можем изменить свойство этого объекта. В приведенном ниже фрагменте кода свойство engine теперь является объектом, и разрешено присвоение нового значения bike.engine.cc. Это означает, что изменение самого свойства engine запрещено, но свойства engine могут быть изменены.

'use strict';
var bike = {
  name: 'SuperSport', 
  maker:'Ducati', 
  engine:{cc: '937', type: 'petrol'}
};
Object.defineProperty(bike, 'engine', {writable: false});
bike.engine.cc = '950';
console.log(bike.engine);
//Output will be
{
  cc: "950",
  type: "petrol"
}

Если мы по-прежнему хотим сделать объект полностью недоступным для записи вместе с его свойствами, мы можем использовать метод Object.freeze(), как показано ниже.

var bike = {
  name: 'SuperSport', 
  maker:'Ducati', 
  engine:{cc: '937', type: 'petrol'}
};
Object.defineProperty(bike, 'engine', {writable: false});
Object.freeze(bike.engine);
bike.engine.cc = '950';
// Throws error in strict mode
console.log(bike.engine.cc); 
// Output will be 937 in non-strict mode

Object.defineProperty() может добавлять новое свойство к объекту, а также изменять существующее свойство.

перечислимый атрибут

По умолчанию свойства любого объекта являются перечислимыми, что означает, что мы можем перебирать их. Но поведение по умолчанию можно изменить, установив для атрибута enumerable значение false для этого свойства, как показано ниже.

var bike = {
  name: 'SuperSport', 
  maker:'Ducati', 
  engine: {cc: '937', type: 'petrol'}
};
Object.defineProperty(bike, 'maker', {enumerable: false});
for(let propName in bike) {
  console.log(propName);
}
// Output:   name engine  (property maker is missing)
var keys = Object.keys(bike);
console.log(keys);
// Output:   ["name", "engine"] (property maker is missing)

В приведенном выше фрагменте кода метод Object.keys() возвращает массив собственных перечислимых свойств данного объекта, но он перечисляет только name и engine property и пропускает свойство maker, поскольку для атрибута enumerable свойства maker установлено значение false .

for..in цикл помогает пройти через весь объект.

настраиваемый атрибут

Мы можем настроить все атрибуты объекта. Но если мы установим для атрибута configurable значение false, это предотвратит изменение определенных атрибутов и предотвратит удаление самого свойства.

var bike = {
  name: 'SuperSport', 
  maker: 'Ducati', 
  engine: '937cc'
};
Object.defineProperty(bike, 'name', {configurable: false});
Object.defineProperty(bike, 'name', {enumerable: false});
// TypeError: Cannot redefine property: name
Object.defineProperty(bike, 'name', {writable: false});
// No error
Object.defineProperty(bike, 'name', {configurable: true});
// Throws error
delete bike.name;
// TypeError: Cannot delete property 'name' (strict mode only)

Согласно приведенному выше фрагменту кода, мы видим, что если атрибуту configurable присвоено значение false. Это не позволяет изменять enumerable и configurable атрибут, но он позволяет изменить атрибут записываемый, и мы не можем удалить свойство, если атрибут настраиваемый установлен на false. (нам нужно удалить первый оператор ошибки, чтобы увидеть последующие ошибки в консоли, потому что выполнение останавливается, как только встречается первая ошибка в коде)

Все атрибуты могут быть изменены одним оператором Object.defineProperty() , как показано ниже, без каких-либо ошибок.

var bike = {
  name: 'SuperSport', 
  maker: 'Ducati', 
  engine: '937cc'
};
Object.defineProperty(bike, 'name', {
  configurable: false,
  writable: false,
  enumerable: false,
  value: '1000cc'
});

получить и установить атрибуты

Атрибуты get и set - это функции, которые работают как получатель и установщик, соответственно, для свойства. При доступе к свойству функция get вызывается без каких-либо аргументов, а когда свойству присваивается какое-либо значение, вызывается функция set со значением, присвоенным свойству. Чтобы создать геттер и сеттер, нам нужно использовать метод Object.defineProperty(), как указано ниже.

var fuel = 'petrol';
var car = {
  name: 'SuperFast', 
  maker: 'Ferrari', 
  engine: 'v12'
};
Object.defineProperty(car, 'engineDetails', {
  get: function() {
    return fuel +" "+ this.engine + " engine";
  },
  set: function(details) {
    let splits = details.split(' ');
    fuel = splits[0];
    this.engine = splits[1];
  }
});
console.log(car.engineDetails);   // output: petrol v12 engine
car.engineDetails = "diesel v8";
console.log(car.engineDetails);   // output: diesel v8 engine

В приведенном выше фрагменте кода engineDetails - это новое свойство объекта car, для которого определены атрибуты get и set. Мы по-прежнему можем получить доступ к этому свойству, как и к любому другому свойству, даже если за сценой оно фактически выполняет функции get и set. Итак, мы видим, что getter и setter очень эффективны, потому что мы можем делать гораздо больше, помимо обычных операций получения и присваивания.

В приведенном выше коде ключевое слово this является объектом контекста выполнения. Чтобы подробнее понять, как работает ключевое слово this, прочтите мою статью JavaScript - Все об этом и новом ключевом слове.

Резюме

Мы узнали все об атрибутах свойств объекта JavaScript. Начиная с дескриптора свойства. Затем мы узнали об использовании атрибутов с возможностью записи, перечисления и настройки. Наконец-то понял силу атрибутов get и set.

Если вам понравился этот пост и он был полезен, пожалуйста, нажмите несколько раз кнопку хлопка 👏, чтобы выразить поддержку, спасибо.