js之defineProperty

对象属性的类型

ECMA-262 使用一些内部特性来描述属性的特征。这些特性是由为 JavaScript 实现引擎的规范定义的。因此,开发者不能在 JavaScript 中直接访问这些特性。为了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来,比如[[Enumerable]]。

属性分两种:数据属性访问器属性,它们共享 configurableenumerable 特性。属性只能是这两种类型之一,不能同时为两者。

数据属性

数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有 4 个特性描述它们的行为:

  • [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  • [[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  • [[Value]]:包含属性实际的值。

访问器属性

访问器属性是不能直接定义的,必须使用 Object.defineProperty()。

访问器属性不包含数据值。相反,它们包含一个获取(getter)函数和一个设置(setter)函数,不过这两个函数不是必需的。在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效的值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。

访问器属性有 4 个特性描述它们的行为:

  • [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  • [[Get]]:获取函数,在读取属性时调用。默认值为 undefined。
  • [[Set]]:设置函数,在写入属性时调用。默认值为 undefined。

属性

要修改属性的默认特性,就必须使用 Object.defineProperty() 方法。这个方法接收 3 个参数:

  • obj:要定义属性的对象。
  • prop:一个字符串或 Symbol,指定了要定义或修改的属性键。
  • descriptor:要定义或修改的属性的描述符。

Object.defineProperty() 允许精确地添加或修改对象上的属性, 默认情况下,使用 Object.defineProperty() 添加的属性是不可写、不可枚举和不可配置的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const person = {};

// 不可枚举/不可删除/不可修改
Object.defineProperty(person, "name", {
value: "John",
});

console.log(person.name); // John
console.log(Object.keys(person)); // 不可枚举

person.name = "Doe"; // 无效,不可修改
console.log(person.name); // John

delete person.name; // 无效,不可删除
console.log(person.name); // John

Object.defineProperty(person, "name", {
enumerable: true,
}); // 报错,不可修改

Configurable 特性

configurable 特性控制属性是否可以从对象中删除以及其特性(除了 value 和 writable)是否可以更改。

configurable 设置为 false,意味着这个属性不能从对象上删除。此外,一个属性被定义为不可配置之后,就不能再变回可配置的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const person = {};
// 可修改/可枚举/可配置
Object.defineProperty(person, "name", {
value: "John",
writable: true,
enumerable: true,
configurable: true,
});

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

Object.defineProperty(person, "name", {
enumerable: false, // 不可枚举
});

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

Object.defineProperty(person, "name", {
configurable: false, // 不可配置
});

Object.defineProperty(person, "name", {
enumerable: true, // 报错,因为不可配置的属性不能修改其特性
});

Enumerable 特性

enumerable 特性定义了属性是否可以被 Object.assign() 或展开运算符所考虑。对于非 Symbol 属性,它还定义了属性是否会在 for…in 循环和 Object.keys() 中显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const person = {};
// 可修改/可枚举/可配置
Object.defineProperty(person, "name", {
value: "John",
enumerable: true,
});
Object.defineProperty(person, "age", {
value: 30,
enumerable: false,
});
Object.defineProperty(person, Symbol.for("gender"), {
value: "male",
enumerable: true,
});
Object.defineProperty(person, Symbol.for("hobby"), {
value: "reading",
enumerable: false,
});
for (const i in person) {
console.log(i); // name
}
console.log(Object.keys(person)); // [ 'name' ]
const newPerson = Object.assign({}, person);
console.log(newPerson); // {name: 'John', Symbol(gender): 'male'}
const { name, ...reset } = person;
console.log(reset); // {Symbol(gender): 'male'}

Writable 特性

writable 特性设置为 falseconfigurable 特性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。尝试对一个不可写的属性进行写入不会改变它,在严格模式下还会导致错误。

writable 特性设置为 falseconfigurable 特性设置为 true 时,该属性的 value 仍然可以使用 defineProperty 进行替换(但不能使用赋值运算符)。

setter 和 getter 特性

  • get:用作属性 getter 的函数,如果没有 getter 则为 undefined。当访问该属性时,将不带参地调用此函数,并将 this 设置为通过该属性访问的对象(因为可能存在继承关系,这可能不是定义该属性的对象)。返回值将被用作该属性的值。

  • set:用作属性 setter 的函数,如果没有 setter 则为 undefined。当该属性被赋值时,将调用此函数,并带有一个参数(要赋给该属性的值),并将 this 设置为通过该属性分配的对象。默认值为 undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let tempName = "John";

const person = {};

Object.defineProperty(person, "name", {
get() {
console.log("get name");
return tempName;
},
set(value) {
console.log("set name");
tempName = value;
},
});

console.log(person.name); // 触发 get 方法,输出: John
person.name = "Jane"; // 触发 set 方法,改变 tempName 的值为 Jane
console.log(person.name); // 触发 get 方法,输出: Jane

参考文档

  • MDN
  • 《JavaScript 高级程序设计 第4版》

js之defineProperty
https://www.my-web.cn/js/defineProperty/
发布于
2026年5月28日
许可协议