[JS]属性标志和属性描述符
到目前为止,属性对我们来说只是一个简单的“键值”对。但对象属性实际上是更灵活且更强大的东西。
对象属性(properties),除 value 外,还有三个特殊的特性(attributes),也就是所谓的“标志”:
writable — 如果为 true,则值可以被修改,否则它是只可读的。
enumerable — 如果为 true,则会被在循环中列出,否则不会被列出。
configurable — 如果为 true,则此属性可以被删除,这些属性也可以被修改,否则不可以。
我们用“常用的方式”创建一个属性时,它们都为 true。
Object.getOwnPropertyDescriptor 方法允许查询有关属性的完整信息。
1 let user = { 2 name: 'jack' 3 } 4 console.log( Object.getOwnPropertyDescriptor(user, 'name') ) 5 // {value: "jack", writable: true, enumerable: true, configurable: true}
为了修改标志,我们可以使用 Object.defineProperty,如果该属性不存在,则会根据标志和给定的值创建该属性。
Object.defineProperty(obj, propertyName, descriptor)
1 let user = { 2 name: 'jack' 3 } 4 5 Object.defineProperty(user, 'name', { 6 value: 'lily', 7 writable: false 8 }) 9 10 console.log(Object.getOwnPropertyDescriptor(user, 'name')) 11 // {value: "lily", writable: false, enumerable: true, configurable: true} 12 13 14 Object.defineProperty(user, 'age', { 15 value: 20 16 }) 17 18 console.log(Object.getOwnPropertyDescriptor(user, 'age')) 19 // {value: 20, writable: false, enumerable: false, configurable: false} 20 // 如果没有提供标志,默认都是false 21 22 user.age = 18 // 该属性不会被修改,并且严格模式下会报错 23 console.log(user) // {name: "lily", age: 20} 24 25 for (let i in user) { 26 console.log(i) // name 属性 age 不会出现,因为它的标志 enumerable 为 false 27 }
不可配置标志(configurable:false)
不可配置性对 defineProperty 施加了一些限制:
不能修改 configurable 标志。
不能修改 enumerable 标志。
不能将 writable: false 修改为 true(反之亦然)。
不能修改访问者属性的 get/set(但是如果没有可以分配它们)。
configurable: false 的思想是防止更改属性标志或删除属性标志,而不是更改它的值。
不可配置但可写的属性的值是可以被更改的。
1 delete user.age 2 3 console.log(user) // {name: "lily", age: 20} 4 5 Object.defineProperty(user, 'age', { // Error 6 writable: true 7 })
Object.defineProperties(obj, descriptors),允许一次定义多个属性。
1 Object.defineProperties(user, { 2 city: { value: '成都', writable: true }, 3 province: { value: '四川', writable: true } 4 }) 5 6 console.log(user) // {name: "lily", age: 20, city: "成都", province: "四川"}
通常我们克隆一个对象是通过赋值的方式来复制,但这样并不能复制标志
如果需要复制标志,我们可以使用 Object.getOwnPropertyDescriptors(obj) 方法。
它与 Object.defineProperties 一起可以用作克隆对象的标志:
1 let person = {} 2 Object.defineProperties(person, { 3 name: { 4 value: 'john', 5 writable: true, 6 enumerable: false, 7 configurable: true 8 }, 9 age: { 10 value: 22, 11 writable: false, 12 enumerable: true 13 } 14 }) 15 16 let clone = Object.defineProperties({}, Object 17 .getOwnPropertyDescriptors(person)) 18 19 console.log(clone) // {age: 22, name: "john"} 20 console.log(Object.getOwnPropertyDescriptors(clone)) 21 /* 22 name: {value: "john", writable: true, enumerable: false, configurable: true} 23 age: {value: 22, writable: false, enumerable: true, configurable: false} 24 */