Object в JavaScript: создание объектов и методы Object.keys, values, entries, assign, freeze

Object в JavaScript: создание объектов и методы Object.keys, values, entries, assign, freeze Полезное

Объект (object) в JavaScript — это значение, которое хранит набор свойств «ключ — значение»: ключ — строка (или Symbol), значение — что угодно, включая функцию. А Object с большой буквы — встроенный конструктор, у которого есть статические методы для работы с любыми объектами: Object.keys, Object.values, Object.entries, Object.assign, Object.freeze и другие.

В Java слово Object значит третье — корневой класс java.lang.Object, от которого наследуются все классы. Ниже разбираем JavaScript: как создать объект, как читать и менять свойства, что делают основные методы Object и на каких ошибках спотыкаются новички. В конце — короткая справка по Java. Все примеры прогнаны в Node.js 24.21 (24.09.2026).

Три значения слова Object

Путаница начинается с того, что одним словом называют разные вещи. Разведем их сразу.

Термин Что это Пример
объект в JS (значение) коллекция свойств «ключ — значение» { name: "Анна", age: 28 }
Object в JS (конструктор) встроенная функция со статическими методами-утилитами Object.keys(user)
java.lang.Object корневой класс Java, его методы есть у каждого объекта obj.equals(other), obj.hashCode()

Важная граница: методы вроде Object.keys вызываются на самом Object, а объект передается аргументом. Записи user.keys() не существует — будет ошибка user.keys is not a function.

Как создать объект в JavaScript

Самый частый способ — литерал в фигурных скобках. Полный пример: создаем объект, читаем, добавляем и удаляем свойства, проверяем наличие ключа и перебираем пары.

// 1. Литерал объекта - самый частый способ
const user = {
  name: "Анна",
  age: 28,
  "last-login": "2026-09-20",
  greet() {
    return `Привет, я ${this.name}`;
  },
};

console.log(user.name);          // точечная нотация
console.log(user["last-login"]); // скобки: для ключей с дефисом и вычисляемых имен
console.log(user.greet());

user.city = "Казань";            // добавить свойство
delete user.age;                 // удалить свойство
console.log("age" in user, Object.hasOwn(user, "city"));

// 2. Статические методы Object
console.log(Object.keys(user));
console.log(Object.values(user).length);
for (const [key, value] of Object.entries(user)) {
  if (typeof value !== "function") console.log(`${key} = ${value}`);
}

Результат в консоли:

Анна
2026-09-20
Привет, я Анна
false true
[ 'name', 'last-login', 'greet', 'city' ]
4
name = Анна
last-login = 2026-09-20
city = Казань

Что здесь важно:

  • const запрещает переприсвоить переменную user, но не запрещает менять сам объект — поэтому user.city = ... работает. Запрет изменений делает Object.freeze (ниже).
  • Точка работает для ключей-идентификаторов, скобки — для ключей с дефисом, пробелом или хранящихся в переменной: user[fieldName].
  • "age" in user ищет свойство и в цепочке прототипов, Object.hasOwn(user, "city") — только собственные свойства. Для словарей обычно нужен второй вариант.

Другие способы создания встречаются реже: new Object() делает то же, что {}, но длиннее; Object.create(proto) создает объект с заданным прототипом; class и функции-конструкторы нужны, когда объектов одного вида много. Прототипы и классы — отдельная большая тема, здесь на них не останавливаемся.

Object.keys, Object.values, Object.entries

Три метода превращают объект в массив, а с массивом уже работают map, filter, reduce:

  • Object.keys(obj) — массив ключей;
  • Object.values(obj) — массив значений;
  • Object.entries(obj) — массив пар [ключ, значение].

Обратную операцию делает Object.fromEntries — собирает объект из пар. Связка entries -> map -> fromEntries — стандартный способ «пройтись по объекту и получить новый объект».

const prices = { apple: 120, pear: 95, plum: 80 };

// entries -> map -> fromEntries: скидка 10% на все
const discounted = Object.fromEntries(
  Object.entries(prices).map(([name, price]) => [name, Math.round(price * 0.9)])
);
console.log(discounted);

// сумма значений
const total = Object.values(prices).reduce((sum, p) => sum + p, 0);
console.log(total);

// порядок ключей: сначала индексы массива по возрастанию, потом строки в порядке добавления
const mixed = { b: 1, 10: "x", a: 2, 2: "y" };
console.log(Object.keys(mixed));
{ apple: 108, pear: 86, plum: 72 }
295
[ '2', '10', 'b', 'a' ]

Последняя строка показывает правило порядка: ключи-индексы массива (целые от 0 до 2^32-2 в канонической записи) идут первыми по возрастанию и становятся строками, остальные строковые ключи — в порядке добавления. Граница правила: "-1", "1.5" и "01" похожи на числа, но индексами не считаются и стоят среди обычных строк в порядке добавления. Все три метода возвращают только собственные перечисляемые свойства со строковыми ключами: унаследованные свойства и Symbol-ключи в результат не попадают.

Object.assign и копирование объекта

Object.assign(target, ...sources) копирует собственные перечисляемые свойства из источников в target и возвращает target. Если ключ встречается несколько раз, побеждает последний источник. Типичное применение — настройки по умолчанию плюс пользовательские.

const defaults = { theme: "light", lang: "ru", notify: { email: true, sms: false } };
const saved = { lang: "en", notify: { sms: true } };

const settings = Object.assign({}, defaults, saved);
console.log(settings);

// поверхностная копия: вложенный объект общий
const copy = Object.assign({}, defaults);
copy.notify.email = false;
console.log(defaults.notify.email);

// глубокая копия
const defaults2 = { theme: "light", notify: { email: true } };
const deep = structuredClone(defaults2);
deep.notify.email = false;
console.log(defaults2.notify.email);

// spread - то же, что assign в новый объект
console.log({ ...defaults, ...saved }.lang);
{ theme: 'light', lang: 'en', notify: { sms: true } }
false
true
en

Здесь две ловушки, и обе видны в выводе.

Первая: слияние неглубокое. Вложенный notify из saved целиком заменил notify из defaults, и поле email пропало. Для вложенных настроек сливать нужно каждый уровень отдельно.

Вторая: копия поверхностная. copy.notify и defaults.notify — один и тот же объект, поэтому правка копии испортила оригинал (false во второй строке). Глубокую копию дает structuredClone — он есть в Node.js начиная с 17-й версии и в современных браузерах, но не копирует функции (на объекте с методом бросит DataCloneError). Пустой объект {} первым аргументом нужен, чтобы не испортить исходник: Object.assign(defaults, saved) изменит сам defaults.

Object.freeze и Object.seal: защита от изменений

Object.freeze(obj) запрещает добавлять, удалять и менять свойства. Object.seal(obj) запрещает добавлять и удалять, но существующие значения менять можно. Object.preventExtensions(obj) запрещает только добавление.

"use strict";
const config = Object.freeze({ port: 8080, db: { host: "localhost" } });

try {
  config.port = 3000;
} catch (e) {
  console.log(e.constructor.name + ": " + e.message);
}
config.db.host = "10.0.0.5"; // вложенный объект не заморожен
console.log(config.port, config.db.host, Object.isFrozen(config), Object.isFrozen(config.db));

function deepFreeze(obj) {
  for (const value of Object.values(obj)) {
    if (value !== null && typeof value === "object") deepFreeze(value);
  }
  return Object.freeze(obj);
}
const cfg = deepFreeze({ port: 8080, db: { host: "localhost" } });
try { cfg.db.host = "x"; } catch (e) { console.log(e.constructor.name); }

const sealed = Object.seal({ a: 1 });
sealed.a = 2;
try { sealed.b = 3; } catch (e) { console.log(e.constructor.name + ": " + e.message); }
console.log(sealed);
TypeError: Cannot assign to read only property 'port' of object '#<Object>'
8080 10.0.0.5 true false
TypeError
TypeError: Cannot add property b, object is not extensible
{ a: 2 }

Поведение зависит от режима. В строгом режиме ("use strict", ES-модули, тело class) запись в замороженный объект бросает TypeError. В нестрогом скрипте та же запись молча игнорируется: config.port = 3000 не изменит значение и не выдаст ошибку. Поэтому баг «значение не поменялось» при заморозке без строгого режима искать труднее.

freeze тоже поверхностный: config.db остался изменяемым. Функция deepFreeze выше рекурсивно замораживает вложенные объекты; она рассчитана на обычные данные без циклических ссылок.

Метод Добавить Удалить Изменить значение Проверка
Object.preventExtensions нет да да Object.isExtensible
Object.seal нет нет да Object.isSealed
Object.freeze нет нет нет Object.isFrozen

Частые ошибки с объектами

Сравнение через ===. Оператор сравнивает ссылки, а не содержимое.

const a = { id: 1 };
const b = { id: 1 };
console.log(a === b);                  // разные объекты
console.log(a === a);
console.log(JSON.stringify(a) === JSON.stringify(b));
console.log(Object.is(NaN, NaN), NaN === NaN, Object.is(0, -0));

const dict = Object.create(null);
dict.toString = "ok";
console.log(Object.keys(dict), "toString" in {});
false
true
true
true false false
[ 'toString' ] true

Два одинаковых по виду объекта не равны: это разные значения в памяти. Сравнение через JSON.stringify работает только в простых случаях: { a: 1, b: 2 } и { b: 2, a: 1 } дадут false из-за разного порядка ключей, а функции и undefined при сериализации теряются. Для надежного сравнения пишут функцию, которая обходит ключи, или берут готовую из библиотеки. Object.is отличается от === только для NaN и -0, содержимое объектов он тоже не сравнивает.

Object.create(null) дает объект без прототипа — чистый словарь, где ключ toString не конфликтует с унаследованным методом. Обычный {} уже «содержит» toString через прототип, что видно по "toString" in {}. Если нужен словарь с произвольными ключами, часто удобнее Map.

Чтение свойства у несуществующего вложенного объекта. Неверный код и результат:

const user = { name: "Анна" };
console.log(user.age);          // нет такого свойства
console.log(user.address?.city);
try {
  console.log(user.address.city);
} catch (e) {
  console.log(e.constructor.name + ": " + e.message);
}
undefined
undefined
TypeError: Cannot read properties of undefined (reading 'city')

Отсутствующее свойство возвращает undefined без ошибки, но обращение к полю у undefined падает. Исправление — опциональная цепочка user.address?.city: она возвращает undefined, если address нет. Текст ошибки приведен для Node.js (движок V8); в Firefox и Safari формулировка другая.

Сводка статических методов Object

Метод Что делает Возвращает
Object.keys(obj) собственные перечисляемые ключи массив строк
Object.values(obj) значения этих ключей массив
Object.entries(obj) пары ключ — значение массив [key, value]
Object.fromEntries(pairs) объект из пар или Map новый объект
Object.assign(target, ...src) копирует свойства в target target
Object.freeze(obj) / Object.seal(obj) запрет изменений (поверхностно) тот же obj
Object.hasOwn(obj, key) есть ли собственное свойство true / false
Object.create(proto) объект с заданным прототипом новый объект
Object.is(a, b) строгое равенство с учетом NaN и -0 true / false

Object в Java: коротко

В Java java.lang.Object — суперкласс всех классов. Его методы есть у любого объекта: equals, hashCode, toString, getClass, а также wait/notify/notifyAll для синхронизации потоков и clone (работает только для классов, реализующих Cloneable). Метод finalize объявлен устаревшим с Java 9, а с Java 18 (JEP 421) помечен к удалению (forRemoval); в Java 25 он еще есть, но в новом коде его не используют — ресурсы закрывают через AutoCloseable и try-with-resources.

Главная практическая связка — equals и hashCode: по умолчанию equals сравнивает ссылки, как === в JavaScript, а коллекции HashSet/HashMap опираются на оба метода. Их переопределяют вместе:

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class Main {
    static class Point {
        final int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
    }

    static class GoodPoint {
        final int x, y;
        GoodPoint(int x, int y) { this.x = x; this.y = y; }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof GoodPoint p)) return false;
            return x == p.x && y == p.y;
        }

        @Override
        public int hashCode() { return Objects.hash(x, y); }
    }

    public static void main(String[] args) {
        Point a = new Point(1, 2), b = new Point(1, 2);
        System.out.println(a.equals(b));
        Set<Point> set = new HashSet<>(Set.of(a));
        System.out.println(set.contains(b));

        GoodPoint c = new GoodPoint(1, 2), d = new GoodPoint(1, 2);
        System.out.println(c.equals(d) + " " + (c == d));
        System.out.println(new HashSet<>(Set.of(c)).contains(d));
    }
}
false
false
true false
true

Запуск: java Main.java (Java 25, однофайловый режим). Без переопределения точки с одинаковыми координатами не равны и не находятся в HashSet; после — равны по содержимому, хотя == по-прежнему сравнивает ссылки. Подробнее про классы и объекты в Java — в статье Java для новичков: что нужно знать о class и object.

Выводы

  • Объект в JavaScript — набор свойств «ключ — значение»; Object — встроенный конструктор со статическими методами; java.lang.Object в Java — корневой класс. Это три разные сущности.
  • Object.keys, values, entries превращают объект в массив, Object.fromEntries собирает обратно; порядок ключей: сначала индексы массива (неотрицательные целые) по возрастанию, затем строки в порядке добавления.
  • Object.assign и spread копируют поверхностно, вложенные объекты остаются общими; для глубокой копии данных есть structuredClone.
  • Object.freeze тоже поверхностный и бросает TypeError только в строгом режиме, в нестрогом запись молча игнорируется.
  • === сравнивает ссылки, а не содержимое объектов — и в JavaScript, и (через == и equals по умолчанию) в Java.

Где применяется / связь с практикой

Объекты — основа почти любого JavaScript-кода: ответы API в JSON, настройки, состояние интерфейса во фронтенд-фреймворках, параметры функций. Методы Object нужны каждый день, когда надо перебрать данные, слить настройки или защитить конфигурацию от случайной правки.

Освойте тему на практике

Если хотите системно пройти основы языка — объекты, массивы, функции, асинхронность и работу с DOM — посмотрите курс «JavaScript-разработчик. Базовый уровень». Разобрать отдельные темы с преподавателями можно на открытых уроках Otus.

FAQ

Чем Object отличается от Map в JavaScript?
Map принимает ключи любого типа (объекты, числа без приведения к строке), хранит порядок вставки для всех ключей, знает размер через size и не имеет унаследованных ключей. Обычный объект удобнее для записей с заранее известными полями и для JSON.

Почему typeof null возвращает «object»?
Это историческая ошибка первой реализации языка, которую сохранили ради совместимости. Проверять объект надежнее условием value !== null && typeof value === "object", как в deepFreeze выше.

Массив — это тоже объект?
Да, typeof [] вернет "object", а Object.keys(["a", "b"]) даст ['0', '1']. Отличить массив от обычного объекта помогает Array.isArray.

OTUS Журнал