Объекты JavaScript: от литералов до классов

Раздел: JavaScript -> Объектно-ориентированное программирование

Объекты в JavaScript

Объект в JavaScript это структура данных, хранящая свойства (ключ-значение) и методы. Свойствами могут быть любые типы, включая функции. Ниже рассмотрены различные способы создания объектов, их особенности и типичные ошибки.

Как создать объект с помощью литерала?

Литеральная нотация самый простой и распространенный способ создания объекта.

const user = {
  name: 'Анна',
  age: 25,
  greet() {
    console.log(`Привет, меня зовут ${this.name}`);
  }
};
user.greet(); // Привет, меня зовут Анна

Javascript object (объекты в javascript)

Объект создается с помощью фигурных скобок. Свойства перечисляются через запятую. Можно использовать короткую запись методов (greet() вместо greet: function() {}).

Типичная ошибка: забыть запятую между свойствами. Это вызывает синтаксическую ошибку. Решение проверять форматирование.

Как создать объект через конструктор new Object()?

const car = new Object();
car.brand = 'Toyota';
car.year = 2020;
car.start = function() { console.log('Двигатель запущен'); };

Public js (модификатор public в javascript)

Этот способ менее удобен для создания объекта с несколькими свойствами, чем литерал. Он полезен, когда нужно создать объект динамически.

Проблема: при добавлении методов через this может возникнуть потеря контекста. Рекомендуется использовать литерал или классы.

Как создать объект с заданным прототипом через Object.create()?

const proto = {
  sayHello() { console.log('Привет!'); }
};
const obj = Object.create(proto);
obj.name = 'Иван';
obj.sayHello(); // Привет!

Javascript class method (методы классов в javascript)

Object.create() позволяет задать прототип для нового объекта. Это ключевой метод для реализации прототипного наследования.

Типичная ошибка: забыть передать null в прототип, если не требуется наследование. При передаче любого другого значения (true, 'string') происходит ошибка. Решение явно указывать прототип или null.

Как создать объект с помощью класса ES6?

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} издает звук.`);
  }
}
const dog = new Animal('Бобик');
dog.speak(); // Бобик издает звук.

Классы синтаксический сахар над прототипным наследованием. Они предоставляют более понятный синтаксис для создания объектов и организации кода.

Частая ошибка: забыть ключевое слово new при создании экземпляра. Это приводит к ошибке (Cannot call a class as a function).

Как создать объект через функцию-конструктор (до ES6)?

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function() {
    console.log('Меня зовут ' + this.name);
  };
}
const alex = new Person('Алекс', 30);
alex.greet(); // Меня зовут Алекс

Функции-конструкторы были основным способом создания объектов до появления классов. Используются с оператором new.

Распространенная проблема: если забыть new, то this будет ссылаться на глобальный объект (window) в нестрогом режиме, что приводит к загрязнению глобальной области видимости. Решение добавить проверку внутри функции (instanceof) или использовать строгий режим.

Как задать свойства с дескрипторами через Object.defineProperty()?

const obj = {};
Object.defineProperty(obj, 'readOnly', {
  value: 42,
  writable: false,
  enumerable: true,
  configurable: false
});
console.log(obj.readOnly); // 42
obj.readOnly = 100; // ошибка в строгом режиме, игнорируется в нестрогом

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

Ошибка: попытка изменить свойство с writable: false в строгом режиме вызывает TypeError. Решение проверять флаги перед изменением.

Как использовать вычисляемые имена свойств?

const key = 'color';
const car = {
  brand: 'Tesla',
  [key]: 'red',
  ['get' + key]() { return this[key]; }
};
console.log(car.color); // red
console.log(car.getcolor()); // red

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

Неправильное использование: попытка использовать переменную без квадратных скобок. Решение всегда заключать динамическое выражение в [].

Расширенные примеры работы с объектами, включая копирование, заморозку, итерацию и современные синтаксические возможности.

Глубокое копирование объекта

Пример
const original = {
  name: 'Мария',
  address: { city: 'Москва', zip: 101000 }
};

// Неглубокое копирование (поверхностное)
const shallowCopy = { ...original };
shallowCopy.address.city = 'Санкт-Петербург';
console.log(original.address.city); // Санкт-Петербург (изменено!)

// Глубокое копирование через JSON
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.address.city = 'Казань';
console.log(original.address.city); // Москва (не изменилось)
original.address.city: Санкт-Петербург (после shallowCopy)
original.address.city: Москва (после deepCopy)

JSON.stringify/JSON.parse подходит для простых объектов без функций, Symbol, undefined. Для более сложных случаев используются библиотеки (lodash.cloneDeep).

Заморозка объекта (Object.freeze)

Пример
const frozenObj = Object.freeze({ prop: 1 });
frozenObj.prop = 2; // игнорируется в нестрогом, ошибка в строгом
console.log(frozenObj.prop); // 1
frozenObj.prop: 1

Object.freeze делает объект полностью неизменяемым: нельзя добавлять, удалять или изменять свойства. Только поверхностно.

Запечатывание объекта (Object.seal)

Пример
const sealedObj = Object.seal({ x: 10 });
sealedObj.y = 20; // не добавится
sealedObj.x = 30; // можно изменить
delete sealedObj.x; // нельзя удалить (false)
sealedObj.y: undefined
sealedObj.x: 30

Object.seal предотвращает добавление и удаление свойств, но изменение существующих разрешено.

Предотвращение расширения (Object.preventExtensions)

Пример
const extObj = { a: 1 };
Object.preventExtensions(extObj);
extObj.b = 2; // не добавится
extObj.a = 10; // можно изменять
console.log(extObj); // { a: 10 }
{ a: 10 }

Object.preventExtensions запрещает добавление новых свойств, но удаление и изменение существующих возможны.

Итерация по собственным свойствам

Пример
const person = { name: 'Ольга', age: 28 };
// for...in (включает унаследованные)
for (let key in person) {
  if (person.hasOwnProperty(key)) console.log(key, person[key]);
}
// Object.keys (только собственные перечисляемые)
Object.keys(person).forEach(k => console.log(k, person[k]));
// Object.entries (возвращает массив [key, value])
for (const [k, v] of Object.entries(person)) {
  console.log(k, v);
}
name Ольга
age 28
name Ольга
age 28
name Ольга
age 28

Для итерации без унаследованных свойств рекомендуется использовать Object.keys, Object.values или Object.entries.

Приватные поля в классах

Пример
class BankAccount {
  #balance = 0;
  constructor(owner) {
    this.owner = owner;
  }
  deposit(amount) {
    this.#balance += amount;
  }
  getBalance() {
    return this.#balance;
  }
}
const account = new BankAccount('Иван');
account.deposit(1000);
console.log(account.getBalance()); // 1000
console.log(account.#balance); // SyntaxError
1000
Uncaught SyntaxError: Private field '#balance' must be declared in an enclosing class

Приватные поля (с #) обеспечивают инкапсуляцию и недоступны снаружи класса.

Геттеры и сеттеры

Пример
class Circle {
  constructor(radius) {
    this._radius = radius;
  }
  get area() {
    return Math.PI * this._radius ** 2;
  }
  set radius(value) {
    if (value < 0) throw new Error('Радиус не может быть отрицательным');
    this._radius = value;
  }
  get radius() {
    return this._radius;
  }
}
const c = new Circle(5);
console.log(c.area); // 78.5398...
c.radius = 10;
console.log(c.area); // 314.159...
78.53981633974483
314.1592653589793

Геттеры и сеттеры позволяют управлять доступом к свойствам, выполняя дополнительную логику при чтении/записи.

Деструктуризация объекта

Пример
const user = { name: 'Елена', age: 32, country: 'RU' };
const { name, age, ...rest } = user;
console.log(name); // Елена
console.log(rest); // { country: 'RU' }

// Вложенная деструктуризация
const data = { person: { firstName: 'Петр', lastName: 'Иванов' } };
const { person: { firstName } } = data;
console.log(firstName); // Петр
Елена
{ country: 'RU' }
Петр

Деструктуризация упрощает извлечение значений из объекта и может применяться для параметров функций.

Объекты в JavaScript - comments

En
Javascript object (javascript)