> For the complete documentation index, see [llms.txt](https://strctr.gitbook.io/programming/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://strctr.gitbook.io/programming/01-languages/javascript/01-language/b-structured/b2-objects/05-property-attributes.md).

# 05. Property Attributes

Помимо имени и значения, свойства обладают атрибутами, определяющие их характеристики. Атрибуты, имеющиеся у всех свойств:

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

```javascript
const c = { key: "value" };
Object.getOwnPropertyDescriptor(c, "key"); // => {value: "value", writable: true, enumerable: true, configurable: true}
```

> Если свойство с данными недоступно для настройки и для записи, нельзя изменить его значение. Однако изменить значение свойства, недоступного для записи можно, если оно доступно для настройки (потому что свойство можно сделать доступным для записи, изменить его значение и затем опять сделать свойство доступным только для чтения).

## Property attributes for `get`/`set` properties

В свойствах с методами доступа (`get/set`-функции) отсутствуют атрибуты `value` и `writable`: их доступность для записи определяется наличием или отсутствием метода записи. Поэтому четырьмя атрибутами свойств с методами доступа являются: метод чтения (`get`), метод записи (`set`), признак доступности для перечисления (`enumerable`) и признак доступности для настройки (`configurable`). Если свойство с методами доступа недоступно для настройки, нельзя изменить его методы чтения и записи и нельзя превратить его в простое свойство с данными.

## Access to property attributes

Получить дескриптор свойства требуемого объекта можно вызовом `Object.getOwnPropertyDescriptor()`, который работает только с собственными свойствами. Чтобы получить атрибуты унаследованного свойства, необходимо явно выполнить обход цепочки прототипов.

```javascript
Object.getOwnPropertyDescriptor({ x: 1 }, "x"); // => {value: 1, writable:true, enumerable:true, configurable:true}
```

Метод `Object.getOwnPropertyDescriptors(obj)` вернет объект с теми же ключами, а значениями будут дескрипторы свойств ключей объекта.

```javascript
const obj = {
  get bar() {
    return "abc";
  }
};
console.log(Object.getOwnPropertyDescriptors(obj));

// Output:
// { [Symbol('foo')]:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }
```

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

```javascript
const o = {};
Object.defineProperty(o, "x", {
  value: 1,
  writable: true,
  enumerable: false,
  configurable: true
});
```

Если возникнет необходимость создать или изменить сразу несколько свойств, можно воспользоваться методом `Object.defineProperties()`. Первым аргументом ему передается объект, который требуется изменить. Вторым аргументом – объект, отображающий имена создаваемых или модифицируемых свойств в дескрипторы этих свойств.

```javascript
const p = Object.defineProperties(
  {},
  {
    x: { value: 1, writable: true, enumerable: true, configurable: true },
    y: { value: 1, writable: true, enumerable: true, configurable: true },
    r: {
      get: function() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
      },
      enumerable: true,
      configurable: true
    }
  }
);
```

Методы `Object.defineProperty()` и `Object.defineProperties()` возбуждают исключение `TypeError`, когда создание или изменение свойств запрещено. Например, при попытке добавить новое свойство в нерасширяемый объект.

## Receipts

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

### Constant attribute

```javascript
Object.defineProperty(user, "name", {
  value: "Anton",
  writable: false, // запретить присвоение "user.name="
  configurable: false // запретить удаление "delete user.name"
});
```

### Non enumerable attribute

```javascript
Object.defineProperty(user, "name", {
  value: "Anton",
  enumerable: false // запретить перебор свойства
});
```
