# 04. Object Properties

## Access to properties

Общий синтаксис:

```javascript
const object = {
  foo: "bar",
  age: 42,
  baz: { myProp: 12 }
};

object.foo; // "bar"
object["age"]; // 42
object.foo = "baz";
```

В JS можно обратиться к любому свойству объекта, даже если его нет. Ошибки не будет. Но если свойство не существует, то вернется `undefined`.

Попытка присвоить значение свойству `p` объекта `o` потерпит неудачу в следующих случаях:

* Объект `o` имеет собственное свойство `p`, доступное только для чтения: нельзя изменить значение свойства, доступного только для чтения.
* Объект `o` имеет унаследованное свойство `p`, доступное только для чтения: унаследованные свойства, доступные только для чтения, невозможно переопределить собственными свойствами с теми же именами.
* Объект `o` не имеет собственного свойства `p`; объект `o` не наследует свойство `p` с методами доступа и атрибут `extensible` объекта o имеет значение `false`.

## Safe property access

Для безопасного доступа к полям объекта можно использовать операторы `||` и `&&` и  `??`. Разница между ними в том, что  `||` и `&&` выполняют более общую проверку на `truthy`/`falsy`, тогда как ??  только на `null` или `undefined`.

```javascript
const o = { x: 1, y: { z: 2 } };
o.x || 3; // 1
o.z || 3; // 3
o.y && o.y.x; // undefined
o.y && o.y.z; // 2
(o.y && o.y.x) || 4; // 4
```

**Optional chaining** в ES2020 позволяет обращаться к вложенным полям объекта при условии, что предыдущие поля в пути определены:

```javascript
// Before
const hashtags = books.entities && books.entities.hashtags

// Optional chaining
const hashtags = books?.entities?.hashtags
```

## Property access in the spec

The operator for getting and setting properties uses the internal operation `ToPropertyKey()`, which works as follows:

* Convert the operand to a primitive via `ToPrimitive()` with the preferred type `String`:
  * A primitive value is returned as it is.
  * Otherwise, the operand is an object. If it has a method `[@@toPrimitive]()`, that method is used to convert it to a primitive value. Symbols have such a method, which returns the wrapped symbol.
  * Otherwise, the operand is converted to a primitive via `toString()` – if it returns a primitive value. Otherwise, `valueOf()` is used – if it returns a primitive value. The preferred type `String` determines that `toString()` is called first, `valueOf()` second.
  * Otherwise, a `TypeError` is thrown.
* If the result of the conversion is a symbol, return it.
* Otherwise, coerce the result to string via `ToString()`.

## Operator `delete`

Единственным способом удалить пару ключ-значение из объекта является оператор `delete`. Если присвоить ключу значение `undefined`, то мы удалим только значение, ключ останется в объекте. Если не использовать `delete`, то ключ будет встречаться при перечислении ключей объекта.

* Оператор `delete` удаляет только собственные свойства и не удаляет унаследованные.
* Нельзя удалить `non-configurable` свойство.
* Чтобы удалить унаследованное свойство, необходимо удалять его в объекте-прототипе, в котором оно определено. Такая операция затронет все объекты, наследующие этот прототип.

```javascript
const mycar = { make: "Honda", model: "Accord", year: 1998 };
delete mycar.make;
"make" in mycar; // returns false
```

Оператор `delete` возвращает `false` если удаляемое свойство является свойством объекта, но не может быть удалено. В остальных случаях оператор возвращает `false`.

На деле, оператор `delete` не связан с управлением памятью. Оператор `delete` полностью удаляет некоторое свойство из указанного объекта, но если оно есть в прототипе -- устанавливается его значение из прототипа. Оператор `delete` позволяет удалять свойства глобального объекта, но не позволяет удалять переменные.

An algorithm for delete is specified roughly like this:

* If operand is not a reference, return `true`
* If object has no direct property with such name, return `true` (where, as we now know, object can be Activation object or Global object)
* If property exists but has `DontDelete`, return `false`

  Otherwise, remove property and return `true`

## Check property existence

Выполнить проверку наличия свойства в объекте можно одним из следующих способов:

### Operator `in`

Оператор `in` требует, чтобы в левом операнде ему было передано имя свойства (в виде строки) и объект в правом операнде. Он возвращает `true`, если объект имеет собственное или унаследованное свойство с этим именем. В противном случае возвращает `undefined`.

```javascript
const c = { make: "Honda", model: "Accord", year: 1998 };
"make" in c; // returns true
"model" in c; // returns true
"google" in c; // returns undefined
```

### `obj.hasOwnProperty()`

Метод `hasOwnProperty()` объекта проверяет, имеет ли объект собственное свойство с указанным именем. Для наследуемых свойств он возвращает `false`.

```javascript
const o = {};
o.prop = "exists";
o.hasOwnProperty("prop"); // returns true
o.hasOwnProperty("toString"); // returns false
o.hasOwnProperty("hasOwnProperty"); // returns false
```

### `obj.propertyIsEnumerable()`

Метод `propertyIsEnumerable()` накладывает дополнительные ограничения по сравнению с `hasOwnProperty()`. Он возвращает `true`, только если указанное свойство является собственным свойством, атрибут `enumerable` которого имеет значение `true`.

```javascript
var a = ["is enumerable"];
a.propertyIsEnumerable(0); // returns true
a.propertyIsEnumerable("length"); // returns false
Math.propertyIsEnumerable("random"); // returns false
this.propertyIsEnumerable("Math"); // returns false
```
