Learn JavaScript

Object.defineProperty() - метод для точной настройки свойства объекта в JavaScript

Object.defineProperty() создаёт новое свойство объекта или перенастраивает существующее.

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

Синтаксис

Object.defineProperty(obj, propertyName, descriptor)

obj - объект, в который нужно добавить или изменить свойство.

propertyName - имя свойства.

descriptor - объект с настройками свойства.

Что делает Object.defineProperty()

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

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
});

console.log(user.name); // "Anna"

Что возвращает метод

Object.defineProperty() возвращает тот же самый объект.

const user = {};
const result = Object.defineProperty(user, "name", {
  value: "Anna",
});

console.log(result === user); // true

Это важно: метод не создаёт новый объект, а работает с существующим.

Обычное свойство через value

Самый простой вариант - создать свойство через value.

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
});

console.log(user.name); // "Anna"

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

writable, enumerable, configurable

Для обычного свойства дескриптор может содержать:

  • value - значение свойства
  • writable - можно ли менять значение
  • enumerable - видно ли свойство в перечислении
  • configurable - можно ли удалять или перенастраивать свойство

Пример:

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
  writable: true,
  enumerable: true,
  configurable: true,
});

По умолчанию флаги равны false

Это один из самых важных моментов.

Если не указать writable, enumerable и configurable, они будут false.

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
});

user.name = "Ivan";

console.log(user.name); // "Anna"
console.log(Object.keys(user)); // []

Здесь свойство:

  • не меняется
  • не попадает в Object.keys()
  • не настраивается свободно как обычное

writable: можно ли менять значение

Если writable: false, значение свойства нельзя изменить.

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
  writable: false,
});

user.name = "Ivan";

console.log(user.name); // "Anna"

Если writable: true, значение можно менять.

enumerable: видно ли свойство при перечислении

Если enumerable: false, свойство не будет видно в Object.keys() и похожих способах перебора.

const user = {};

Object.defineProperty(user, "secret", {
  value: "123",
  enumerable: false,
});

console.log(Object.keys(user)); // []
console.log(user.secret); // "123"

Свойство есть, но в перечислении его не видно.

configurable: можно ли перенастраивать свойство

Если configurable: false, свойство нельзя удалить и сложно перенастроить.

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
  configurable: false,
});

delete user.name;

console.log(user.name); // "Anna"

Это полезно, когда нужно жёстко закрепить поведение свойства.

Getter и setter

Через Object.defineProperty() можно создавать не только обычные свойства, но и accessor properties.

const user = {
  firstName: "Anna",
  lastName: "Smith",
};

Object.defineProperty(user, "fullName", {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
});

console.log(user.fullName); // "Anna Smith"

Здесь fullName не хранит значение напрямую, а вычисляется через getter.

Setter

Через setter можно управлять записью в свойство.

const user = {
  firstName: "Anna",
  lastName: "Smith",
};

Object.defineProperty(user, "fullName", {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
  set(value) {
    const [first, last] = value.split(" ");
    this.firstName = first;
    this.lastName = last;
  },
});

user.fullName = "Ivan Petrov";

console.log(user.firstName); // "Ivan"
console.log(user.lastName);  // "Petrov"

Нельзя смешивать value/writable и get/set

Это важное правило.

В одном дескрипторе нельзя одновременно использовать:

  • value или writable
  • и get или set

Такой код приведёт к ошибке:

Object.defineProperty(obj, "x", {
  value: 1,
  get() {
    return 2;
  },
});

Нужно выбрать либо обычное свойство, либо accessor property.

Object.defineProperty() и обычное присваивание

Обычная запись:

user.name = "Anna";

создаёт обычное свойство с привычными настройками.

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

Object.defineProperty(user, "name", {
  value: "Anna",
  writable: false,
  enumerable: false,
});

То есть это инструмент для более тонкой настройки.

Проверка через Object.getOwnPropertyDescriptor()

Очень удобно смотреть результат через дескриптор.

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
  enumerable: false,
});

console.log(Object.getOwnPropertyDescriptor(user, "name"));

Так можно сразу увидеть, какие флаги реально установились.

Object.defineProperty() не меняет прототип

Метод работает только со свойством самого объекта.

Он не связан с прототипной цепочкой напрямую.

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
});

console.log(user.name); // "Anna"

Это просто настройка свойства конкретного объекта.

Массивы тоже работают

Массив - это тоже объект, поэтому метод работает и с ним.

const arr = ["a", "b"];

Object.defineProperty(arr, "0", {
  value: "x",
});

console.log(arr); // ["x", "b"]

Но с массивами таким способом обычно работают осторожно, чтобы не получить странное поведение.

Когда это полезно

Object.defineProperty() удобно использовать, когда:

  • нужно скрыть свойство из перечисления
  • нужно запретить изменение значения
  • нужно создать getter/setter
  • нужно точно настроить поведение свойства

Пример:

const config = {};

Object.defineProperty(config, "version", {
  value: 1,
  writable: false,
  enumerable: true,
  configurable: false,
});

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

Забывать, что writable/enumerable/configurable по умолчанию false

const user = {};

Object.defineProperty(user, "name", {
  value: "Anna",
});

console.log(Object.keys(user)); // []

Это поведение часто удивляет.

Пытаться смешать value и get/set

Object.defineProperty(obj, "x", {
  value: 1,
  get() {
    return 2;
  },
});

Такой дескриптор недопустим.

Думать, что метод создаёт новый объект

const user = {};
const result = Object.defineProperty(user, "name", {
  value: "Anna",
});

console.log(result === user); // true

Метод изменяет существующий объект.

Быстрая памятка

  • Object.defineProperty() создаёт или перенастраивает свойство объекта
  • метод возвращает тот же самый объект
  • через дескриптор можно управлять value, writable, enumerable, configurable
  • через get и set можно создавать accessor properties
  • по умолчанию многие флаги будут false

Коротко

Object.defineProperty() - это способ точно настроить свойство объекта в JavaScript. Главное, что стоит запомнить: этот метод нужен не для обычного присваивания, а для управления дескриптором свойства, его видимостью, изменяемостью и поведением через getter/setter.