💡 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 类型的和不可枚举的属性在内的 所有属性描述符。
