Object.defineProperty()
Baseline Широко доступно
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна во всех браузерах с июля 2015 года.
Статический метод Object.defineProperty() определяет новое свойство непосредственно в объекте или изменяет существующее свойство в объекте и возвращает этот объект.
Попробуйте
const object = {};
Object.defineProperty(object, "foo", {
value: 42,
writable: false,
});
object.foo = 77;
// Throws an error in strict mode
console.log(object.foo);
// Expected output: 42
Синтаксис
Object.defineProperty(obj, prop, descriptor)
Параметры
-
obj - Объект, в котором определяется свойство.
-
prop - Строка или
Symbol, определяющая ключ свойства, которое нужно определить или изменить. -
descriptor - Дескриптор определяемого или изменяемого свойства.
Возвращаемое значение
Объект, переданный в функцию, с добавленным или измененным указанным свойством.
Описание
Object.defineProperty() позволяет точно добавлять или изменять свойства объекта. Обычное добавление свойств через присваивание создает свойства, которые отображаются при перечислении свойств (for...in, Object.keys() и т. д.), значения которых могут быть изменены и которые могут быть удалены. Этот метод позволяет изменить эти дополнительные детали по сравнению с их значениями по умолчанию. По умолчанию свойства, добавленные с помощью Object.defineProperty(), не являются записываемыми, не перечисляются и не конфигурируемы. Кроме того, Object.defineProperty() использует внутренний метод [[DefineOwnProperty]] вместо [[Set]], поэтому он не вызывает сеттеры, даже если свойство уже существует.
Дескрипторы свойств, присутствующие в объектах, имеют два основных вида: дескрипторы данных и дескрипторы доступа. Дескриптор данных — это свойство со значением, которое может быть записываемым или нет. Дескриптор доступа — это свойство, описываемое парой функций getter-setter. Дескриптор должен быть одного из этих двух типов; он не может быть обоими.
Как дескрипторы данных, так и дескрипторы доступа являются объектами. Они разделяют следующие необязательные ключи (обратите внимание: упомянутые здесь значения по умолчанию относятся к случаю определения свойств с помощью Object.defineProperty()):
-
configurable - когда установлено в
false,- тип этого свойства нельзя изменить с типа свойства данных на тип свойства доступа, и
- свойство нельзя удалить, и
- другие атрибуты его дескриптора нельзя изменить (однако, если это дескриптор данных с
writable: true, тоvalueможно изменить, аwritableможно изменить наfalse).
По умолчанию
false. -
enumerable true, если и только если это свойство отображается при перечислении свойств соответствующего объекта. По умолчаниюfalse.
Дескриптор данных также имеет следующие необязательные ключи:
-
value - Значение, связанное со свойством. Может быть любым допустимым значением JavaScript (число, объект, функция и т. д.). По умолчанию
undefined. -
writable true, если значение, связанное со свойством, может быть изменено с помощью оператора присваивания. По умолчаниюfalse.
Дескриптор доступа также имеет следующие необязательные ключи:
-
get - Функция, выполняющая роль геттера для свойства, или
undefined, если геттера нет. При доступе к свойству эта функция вызывается без аргументов, аthisустанавливается равным объекту, через который осуществляется доступ к свойству (это может быть не объект, на котором определено свойство, из-за наследования). Возвращаемое значение будет использоваться в качестве значения свойства. По умолчаниюundefined. -
set - Функция, выполняющая роль сеттера для свойства, или
undefined, если сеттера нет. При присваивании свойству эта функция вызывается с одним аргументом (значение, присваиваемое свойству), аthisустанавливается равным объекту, через который осуществляется присваивание свойству. По умолчаниюundefined.
Если дескриптор не содержит ни одного из ключей value, writable, get и set, он рассматривается как дескриптор данных. Если дескриптор является одновременно дескриптором данных (поскольку он имеет value или writable) и дескриптором доступа (поскольку он имеет get или set), генерируется исключение.
Эти атрибуты не обязательно являются собственными свойствами дескриптора. Унаследованные свойства также будут учитываться. Чтобы гарантировать сохранение этих значений по умолчанию, вы можете предварительно заморозить существующие объекты в цепочке прототипов объекта дескриптора, явно указать все параметры или создать объект null-prototype.
const obj = {};
// 1. Using a null prototype: no inherited properties
const descriptor = Object.create(null);
descriptor.value = "static";
// not enumerable, not configurable, not writable as defaults
Object.defineProperty(obj, "key", descriptor);
// 2. Being explicit by using a throw-away object literal with all attributes present
Object.defineProperty(obj, "key2", {
enumerable: false,
configurable: false,
writable: false,
value: "static",
});
// 3. Prevents adding or removing the object prototype properties
// (value, get, set, enumerable, writable, configurable)
Object.freeze(Object.prototype);
Когда свойство уже существует, Object.defineProperty() пытается изменить свойство в соответствии со значениями в дескрипторе и текущей конфигурацией свойства.
Если старый дескриптор имел атрибут configurable, установленный в false, свойство считается неконфигурируемым. Невозможно изменить какой-либо атрибут неконфигурируемого свойства доступа, и невозможно переключиться между типами свойств данных и доступа. Для свойств данных с writable: true можно изменить значение и изменить атрибут writable с true на false. TypeError генерируется при попытках изменить атрибуты неконфигурируемых свойств (за исключением value и writable, если разрешено), за исключением случаев, когда определяется значение, совпадающее с исходным значением для свойства данных.
Когда текущее свойство является конфигурируемым, определение атрибута как undefined фактически удаляет его. Например, если o.k является свойством доступа, Object.defineProperty(o, "k", { set: undefined }) удалит сеттер, в результате чего k будет иметь только геттер и станет только для чтения. Если атрибут отсутствует в новом дескрипторе, значение старого атрибута дескриптора сохраняется (оно не будет неявно переопределено в undefined). Возможно переключение между свойствами данных и свойствами доступа, предоставляя дескриптор другого «типа». Например, если новый дескриптор является дескриптором данных (с value или writable), атрибуты get и set исходного дескриптора будут оба удалены.
Примеры
Создание свойства
Когда указанное свойство не существует в объекте, Object.defineProperty() создает новое свойство, как описано. Поля могут быть опущены из дескриптора, и будут использованы значения по умолчанию для этих полей.
const o = {}; // Creates a new object
// Example of an object property added
// with defineProperty with a data property descriptor
Object.defineProperty(o, "a", {
value: 37,
writable: true,
enumerable: true,
configurable: true,
});
// 'a' property exists in the o object and its value is 37
// Example of an object property added
// with defineProperty with an accessor property descriptor
let bValue = 38;
Object.defineProperty(o, "b", {
get() {
return bValue;
},
set(newValue) {
bValue = newValue;
},
enumerable: true,
configurable: true,
});
o.b; // 38
// 'b' property exists in the o object and its value is 38
// The value of o.b is now always identical to bValue,
// unless o.b is redefined
// You cannot try to mix both:
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get() {
return 0xdeadbeef;
},
});
// throws a TypeError: value appears
// only in data descriptors,
// get appears only in accessor descriptors
Изменение свойства
При изменении существующего свойства текущая конфигурация свойства определяет, успешно ли выполняется операция, ничего не делает или генерирует TypeError.
Атрибут записываемости
Когда атрибут свойства writable равен false, свойство считается «незаписываемым». Его нельзя переприсвоить. Попытка записи в незаписываемое свойство не изменяет его и приводит к ошибке в строгом режиме.
const o = {}; // Creates a new object
Object.defineProperty(o, "a", {
value: 37,
writable: false,
});
console.log(o.a); // 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a); // 37; the assignment didn't work
// strict mode
(() => {
"use strict";
const o = {};
Object.defineProperty(o, "b", {
value: 2,
writable: false,
});
o.b = 3; // throws TypeError: "b" is read-only
return o.b; // returns 2 without the line above
})();
Атрибут перечисляемости
Атрибут свойства enumerable определяет, учитывается ли свойство оператором Object.assign() или оператором spread. Для свойств, не являющихся Symbol, он также определяет, отображается ли оно в цикле for...in и Object.keys(). Дополнительную информацию см. в Enumerability and ownership of properties.
const o = {};
Object.defineProperty(o, "a", {
value: 1,
enumerable: true,
});
Object.defineProperty(o, "b", {
value: 2,
enumerable: false,
});
Object.defineProperty(o, "c", {
value: 3,
}); // enumerable defaults to false
o.d = 4; // enumerable defaults to true when creating a property by setting it
Object.defineProperty(o, Symbol.for("e"), {
value: 5,
enumerable: true,
});
Object.defineProperty(o, Symbol.for("f"), {
value: 6,
enumerable: false,
});
for (const i in o) {
console.log(i);
}
// Logs 'a' and 'd' (always in that order)
Object.keys(o); // ['a', 'd']
o.propertyIsEnumerable("a"); // true
o.propertyIsEnumerable("b"); // false
o.propertyIsEnumerable("c"); // false
o.propertyIsEnumerable("d"); // true
o.propertyIsEnumerable(Symbol.for("e")); // true
o.propertyIsEnumerable(Symbol.for("f")); // false
const p = { ...o };
p.a; // 1
p.b; // undefined
p.c; // undefined
p.d; // 4
p[Symbol.for("e")]; // 5
p[Symbol.for("f")]; // undefined
Атрибут конфигурируемости
Атрибут configurable управляет тем, можно ли удалить свойство из объекта и можно ли изменять его атрибуты (кроме value и writable).
Этот пример демонстрирует неконфигурируемое свойство доступа.
const o = {};
Object.defineProperty(o, "a", {
get() {
return 1;
},
configurable: false,
});
Object.defineProperty(o, "a", {
configurable: true,
}); // throws a TypeError
Object.defineProperty(o, "a", {
enumerable: true,
}); // throws a TypeError
Object.defineProperty(o, "a", {
set() {},
}); // throws a TypeError (set was undefined previously)
Object.defineProperty(o, "a", {
get() {
return 1;
},
}); // throws a TypeError
// (even though the new get does exactly the same thing)
Object.defineProperty(o, "a", {
value: 12,
}); // throws a TypeError
// ('value' can be changed when 'configurable' is false, but only when the property is a writable data property)
console.log(o.a); // 1
delete o.a; // Nothing happens; throws an error in strict mode
console.log(o.a); // 1
Если бы атрибут configurable o.a был true, никаких ошибок не возникло бы, и свойство было бы удалено в конце.
Этот пример демонстрирует неконфигурируемое, но записываемое свойство данных. value свойства по-прежнему можно изменять, а writable по-прежнему можно переключать с true на false.
const o = {};
Object.defineProperty(o, "b", {
writable: true,
configurable: false,
});
console.log(o.b); // undefined
Object.defineProperty(o, "b", {
value: 1,
}); // Even when configurable is false, because the object is writable, we may still replace the value
console.log(o.b); // 1
o.b = 2; // We can change the value with assignment operators as well
console.log(o.b); // 2
// Toggle the property's writability
Object.defineProperty(o, "b", {
writable: false,
});
Object.defineProperty(o, "b", {
value: 1,
}); // TypeError: because the property is neither writable nor configurable, it cannot be modified
// At this point, there's no way to further modify 'b'
// or restore its writability
Этот пример демонстрирует конфигурируемое, но незаписываемое свойство данных. value свойства все еще может быть заменено на defineProperty (но не с помощью операторов присваивания), а writable может быть переключено.
const o = {};
Object.defineProperty(o, "b", {
writable: false,
configurable: true,
});
Object.defineProperty(o, "b", {
value: 1,
}); // We can replace the value with defineProperty
console.log(o.b); // 1
o.b = 2; // throws TypeError in strict mode: cannot change a non-writable property's value with assignment
Этот пример демонстрирует неконфигурируемое и незаписываемое свойство данных. Нет никакого способа обновить какой-либо атрибут свойства, включая его value.
const o = {};
Object.defineProperty(o, "b", {
writable: false,
configurable: false,
});
Object.defineProperty(o, "b", {
value: 1,
}); // TypeError: the property cannot be modified because it is neither writable nor configurable.
Добавление свойств и значения по умолчанию
Важно учитывать способ применения значений атрибутов по умолчанию. Часто существует разница между использованием аксессоров свойств для присваивания значения и использованием Object.defineProperty(), как показано в примере ниже.
const o = {};
o.a = 1;
// is equivalent to:
Object.defineProperty(o, "a", {
value: 1,
writable: true,
configurable: true,
enumerable: true,
});
// On the other hand,
Object.defineProperty(o, "a", { value: 1 });
// is equivalent to:
Object.defineProperty(o, "a", {
value: 1,
writable: false,
configurable: false,
enumerable: false,
});
Пользовательские сеттеры и геттеры
Приведенный ниже пример показывает, как реализовать самоархивирующийся объект. При установке свойства temperature массив archive получает запись в журнале.
function Archiver() {
let temperature = null;
const archive = [];
Object.defineProperty(this, "temperature", {
get() {
console.log("get!");
return temperature;
},
set(value) {
temperature = value;
archive.push({ val: temperature });
},
});
this.getArchive = () => archive;
}
const arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
В этом примере геттер всегда возвращает одно и то же значение.
const pattern = {
get() {
return "I always return this string, whatever you have assigned";
},
set() {
this.myName = "this is my name string";
},
};
function TestDefineSetAndGet() {
Object.defineProperty(this, "myProperty", pattern);
}
const instance = new TestDefineSetAndGet();
instance.myProperty = "test";
console.log(instance.myProperty);
// I always return this string, whatever you have assigned
console.log(instance.myName); // this is my name string
Наследование свойств
Если свойство доступа унаследовано, его методы get и set будут вызываться при доступе и изменении свойства в объектах-потомках. Если эти методы используют переменную для хранения значения, это значение будет общим для всех объектов.
function MyClass() {}
let value;
Object.defineProperty(MyClass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
},
});
const a = new MyClass();
const b = new MyClass();
a.x = 1;
console.log(b.x); // 1
Это можно исправить, сохранив значение в другом свойстве. В методах get и set this указывает на объект, который используется для доступа или изменения свойства.
function MyClass() {}
Object.defineProperty(MyClass.prototype, "x", {
get() {
return this.storedX;
},
set(x) {
this.storedX = x;
},
});
const a = new MyClass();
const b = new MyClass();
a.x = 1;
console.log(b.x); // undefined
В отличие от свойств доступа, свойства данных всегда устанавливаются в самом объекте, а не в прототипе. Однако, если унаследовано незаписываемое свойство данных, оно по-прежнему не может быть изменено в объекте.
function MyClass() {}
MyClass.prototype.x = 1;
Object.defineProperty(MyClass.prototype, "y", {
writable: false,
value: 1,
});
const a = new MyClass();
a.x = 2;
console.log(a.x); // 2
console.log(MyClass.prototype.x); // 1
a.y = 2; // Ignored, throws in strict mode
console.log(a.y); // 1
console.log(MyClass.prototype.y); // 1
Спецификации
Совместимость с браузерами
| Desktop | Mobile | Server | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on iOS | Samsung Internet | WebView Android | WebView on iOS | Bun | Deno | Node.js | |
defineProperty |
5 |
12 |
4 |
11.6 |
5.1Также поддерживается в Safari 5, но не на DOM-объектах. |
18 |
4 |
12 |
6Также поддерживается в Safari для iOS 4.2, но не на DOM-объектах. |
1.0 |
4.4 |
6Также поддерживается в Safari для iOS 4.2, но не на DOM-объектах. |
1.0.0 |
1.0 |
0.10.0 |
См. также
- Enumerability and ownership of properties
Object.defineProperties()Object.prototype.propertyIsEnumerable()Object.getOwnPropertyDescriptor()getsetObject.create()Reflect.defineProperty()
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty