💡 JavaScript 对象属性标志和属性描述符
# 属性标志
对象的属性(properties)除了 value
值以外,还有三个特殊的 特性(attributes),也是 标志 :
writable
可写:为true
,表示该值可以修改。否则为只可读的。enumerable
可迭代:如果为true
,则会被在循环中列出。否则不会被列出。configurable
:如果为true
,则此属性可以被删除,这些特性也可以被修改,否则不可以。
在使用 常用的方式 创建一个属性时,这些标志 默认都为 true
。
要获取这些标志的信息,可以使用
Object.getOwnPropertyDescriptor
方式查询有关属性的完整信息,语法为:let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName)
1obj
:要获取信息的对象。propertyName
:属性名称。
该方法的返回值是一个 「属性描述符」 对象, 包含 值 和所有的标志。
🌰 例子:
let user = { name: "John" }; let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); console.log(JSON.stringify(descriptor, null, 2)) /* "value": "John", "writable": true, "enumerable": true, "configurable": true */
1
2
3
4
5
6
7
8
9
10
11
12要 修改 这些标志,可以使用
Object.defineProperty
,语法为:Object.defineProperty(obj, propertyName, descriptor)
1obj
、propertyName
:同上。descriptor
:要应用的 属性描述符对象。
如果该属性存在,
defineProperty
会更新其标志,否则会使用 特定的值 和标志创建属性,这种情况下没有提供标志则 假定 它为false
。🌰 例子:
let user = {} Object.defineProperty(user, "name", { value: "John" }) let descriptor = Object.getOwnPropertyDescriptor(user, name) console.log(JSON.stringify(descriptor, null, 2)) /* value": "John", "writable": false, "enumerable": false, "configurable": false */
1
2
3
4
5
6
7
8
9
10
11
12
13
14这种创建属性的方式与上面常用的普通创建属性的方法进行比较,现在所有的 标志 都被假定为
false
,可以在创建属性时通过descriptor
中设置为true
。
# 只读属性
可以通过将 标志 writable
设置为 false
,使对象中的属性为只读(即不能重新赋值)。
🌰 例子:
let user = {
name: 'John'
}
Object.defineProperty(user, 'name', {
writable: false
})
user.name = 'Pete' // Error
2
3
4
5
6
7
8
9
🌰 例子:
let user = { };
Object.defineProperty(user, "name", {
value: "John",
// 对于新创建的属性需要列出要为 true 的标志,否则为 false
enumerable: true,
configurable: true
});
console.log(user.name); // John
user.name = "Pete"; // Error
2
3
4
5
6
7
8
9
10
11
# 不可枚举属性
🌰 例子:一般属性自带的 toString()
方法默认是不可枚举的,即在 for ... in
中不会出现。但是如果自定义重写的 toString()
时,它会出现在 for ... in
中。
let user = {
name: 'Simon',
toString() {
return this.name
}
}
for(let key in user) console.log(key) // name, toString
2
3
4
5
6
7
8
将 toString
设置为不可枚举:
Object.defineProperty(user, "toString", {
enumerable: false
})
for(let key in user) console.log(key) // name
console.log(Object.keys(user)) // name
2
3
4
5
6
7
# 不可配置属性
不可配置的属性不能被删除,它的 特性(attribute) 不能被修改。
🌰 例子 / Math.PI
是可读的、不可枚举、不可配置的:
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
console.log(JSON.stringify(descriptor, null, 2 ))
/*
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
*/
2
3
4
5
6
7
8
9
10
所以,并不能修改
Math.PI
的值,或者覆盖它。也无法修改它的特性,writable
或者enumerable
。
注意
可以从例子中看出,只要将 configurable
设置为 false
,之后再也无法通过 defineProperty
更改回来。
注意:configurable: false
防止更改和删除属性标志,但是允许更改对象的值。
🌰 例子:
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
configurable: false
});
user.name = "Pete" // 正常工作
delete user.name // 出错
2
3
4
5
6
7
8
9
10
将 name
属性同时设置为 writable: false
,此时该属性就会像内建的 Math.PI
一样,永不可改:
Object.defineProperty(user, "name", {
writable: false,
configurable: false
});
user.name = "Pete" // 出错
delete user.name // 出错
Object.defineProperty(user, "name", { value: "Pete" }); // 出错
2
3
4
5
6
7
8
提示
唯一可以修改的特性( writable
):对于不可配置的属性,可以将 writbale
设置为 false
,以防止其值被修改(以添加另一层保护)。但无法反向行之。
# Object.defineProperties(obj, descriptors)
该方法允许一次定义多个属性,语法为:
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
2
3
4
5
🌰 例子:
Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
})
2
3
4
5
# Object.getOwnPropertyDescriptors(obj)
该方法可以一次获取 所有属性描述符。
通常在克隆一个对象时,使用 for key in obj
遍历赋值的方式进行克隆,但是不能复制 包括 symbol 类型的和不可枚举属性在内的所有 属性描述符。
深层完全克隆方式:可以与 Object.defineProperties
一起用作 克隆对象 的 「标志感知」 方式:
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
因为
Object.getOwnPropertyDescriptors(obj)
可以返回包含 symbol 类型的和不可枚举的属性在内的 所有属性描述符。