Объекты 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); // 1frozenObj.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); // SyntaxError1000 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' }
ПетрДеструктуризация упрощает извлечение значений из объекта и может применяться для параметров функций.