Свойства объектов: литералы, дескрипторы, аксессоры

Раздел: JavaScript -> Объекты

Основной способ работы со свойствами объектов

Наиболее простой и часто используемый метод - определение свойств непосредственно в литерале объекта и доступ через точку. Пример:

let user = {
  name: 'Анна',
  age: 25
};
console.log(user.name); // Анна
user.age = 26;
delete user.name;

Js object (объекты javascript)

Такой подход подходит для большинства задач, когда свойства известны заранее и их поведение не требует специальной настройки. Однако он не позволяет управлять атрибутами свойства: возможностью перезаписи, удаления, перечисления.

Как задать свойство с именем, полученным из переменной?

Использование квадратных скобок (computed property names) позволяет вычислять имя свойства на лету.

let key = 'email';
let user = {
  [key]: 'user@example.com'
};
console.log(user.email); // user@example.com

Property js (свойства объектов в javascript)

Типичная ошибка: попытка использовать точку с переменной (user.key) - это обратится к свойству с именем "key", а не к значению переменной.

Как сделать свойство только для чтения?

Метод Object.defineProperty позволяет задать дескриптор свойства, в том числе writable.

let user = {};
Object.defineProperty(user, 'id', {
  value: 123,
  writable: false,
  configurable: true,
  enumerable: true
});
user.id = 456; // игнорируется в строгом режиме TypeError
console.log(user.id); // 123
В нестрогом режиме запись в read-only свойство игнорируется без ошибки. В строгом - выбрасывается TypeError. Рекомендуется всегда использовать строгий режим для отлова таких ошибок.

Как скрыть свойство от перечисления в for...in или Object.keys?

Установка enumerable: false делает свойство невидимым для перебора.

let user = { name: 'Анна' };
Object.defineProperty(user, 'secret', {
  value: 'confidential',
  enumerable: false
});
for(let key in user) console.log(key); // только "name"
console.log(Object.keys(user)); // ['name']
Свойство всё равно можно прочитать напрямую: user.secret. Оно просто не участвует в итерациях и не сериализуется в JSON.

Как создать свойство, значение которого вычисляется при обращении?

Использование геттера (get) и опционально сеттера (set).

let user = {
  firstName: 'Иван',
  lastName: 'Петров',
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  set fullName(value) {
    [this.firstName, this.lastName] = value.split(' ');
  }
};
console.log(user.fullName); // Иван Петров
user.fullName = 'Анна Сидорова';
console.log(user.firstName); // Анна
Геттер не принимает аргументов, сеттер ровно один. Если внутри геттера используется this, он будет ссылаться на объект, на котором вызывается свойство, но при передаче функции отдельно контекст теряется.

Как запретить добавление новых свойств в объект?

Методы Object.preventExtensions, Object.seal, Object.freeze постепенно усиливают защиту.

let user = { name: 'Иван' };
Object.preventExtensions(user);
user.age = 30; // игнорируется или TypeError
console.log(user.age); // undefined

Object.seal(user); // дополнительно делает все свойства non-configurable
Object.freeze(user); // ещё и writable: false
При попытке добавить свойство в замороженный объект ошибка возникает только в строгом режиме. Также freeze не затрагивает вложенные объекты.

Как создать уникальное имя свойства, не конфликтующее с другими?

Тип Symbol позволяет создавать уникальные идентификаторы.

let sym = Symbol('id');
let user = {
  [sym]: 123,
  name: 'Анна'
};
console.log(user[sym]); // 123
console.log(Object.keys(user)); // ['name'] - Symbol не отображается
Symbol свойства не видны в обычных переборах, но доступны через Object.getOwnPropertySymbols. При JSON.stringify они игнорируются.

Пример 1: Геттер/сеттер с проверкой

В этом примере свойство password использует геттер для маскировки и сеттер для валидации длины.

Пример
let user = {
  _password: '',
  set password(value) {
    if (value.length < 6) {
      throw new Error('Пароль должен быть не менее 6 символов');
    }
    this._password = value;
  },
  get password() {
    return this._password ? '***' : 'не установлен';
  }
};
user.password = 'секрет123';
console.log(user.password); // ***
console.log(user._password); // 'секрет123'
***

Пример 2: Object.defineProperties для нескольких свойств

Метод позволяет задать дескрипторы сразу для группы свойств с разными характеристиками.

Пример
let book = {};
Object.defineProperties(book, {
  title: { value: 'JavaScript: The Good Parts', writable: false, enumerable: true },
  year: { value: 2008, writable: true, enumerable: false },
  getInfo: {
    get: function() { return `${this.title} (${this.year})`; },
    enumerable: true
  }
});
console.log(book.title); // JavaScript: The Good Parts
console.log(Object.keys(book)); // ['title', 'getInfo']
['title', 'getInfo']

Пример 3: Символы для скрытых метаданных

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

Пример
const secretKey = Symbol('secret');
function createUser(name) {
  let user = { name };
  user[secretKey] = Math.random();
  return user;
}
let u = createUser('Анна');
console.log(Object.getOwnPropertySymbols(u)); // [ Symbol(secret) ]
console.log(JSON.stringify(u)); // {"name":"Анна"}
[ Symbol(secret) ]
{"name":"Анна"}

Пример 4: Динамические имена свойств с Object.fromEntries

Удобный способ создания объекта из массива пар ключ-значение, особенно при обработке данных.

Пример
let entries = [['a', 1], ['b', 2]];
let obj = Object.fromEntries(entries);
console.log(obj); // { a: 1, b: 2 }
let keyValuePairs = ['x', 'y', 'z'].map(k => [k, k.toUpperCase()]);
let obj2 = Object.fromEntries(keyValuePairs);
console.log(obj2); // { x: 'X', y: 'Y', z: 'Z' }
{ a: 1, b: 2 }
{ x: 'X', y: 'Y', z: 'Z' }

Пример 5: Свойство-аксессор с кэшированием вычислений

Геттер area хранит вычисленное значение, сбрасывая его при изменении зависимых свойств.

Пример
let rectangle = {
  _width: 0,
  _height: 0,
  set width(w) { this._width = w; this._area = undefined; },
  get width() { return this._width; },
  set height(h) { this._height = h; this._area = undefined; },
  get height() { return this._height; },
  get area() {
    if (this._area === undefined) {
      this._area = this._width * this._height;
    }
    return this._area;
  }
};
rectangle.width = 5;
rectangle.height = 3;
console.log(rectangle.area); // 15
rectangle.width = 7;
console.log(rectangle.area); // 21
15
21

Свойства объектов в JavaScript - comments

En
Property js (javascript)