- A+
属性类型
ES5定义了内部才用的特性时,描述了属性的各种特征。ES5定义这些特征是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。为了表示特性时内部值,该规范把他们放在了两对中括号中,例如 [[Enumerable]]。
ECMAScript 中有两种属性:数据属性和访问器属性。
数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性:
特性 | 描述 | 默认值 |
---|---|---|
[[Configurable]](可配置的) | 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性 | true |
[[Enumerable]](可枚举的) | 表示能否通过 for-in 循环返回属性 | true |
[[Writable]](可写入的) | 表示能否修改属性的值 | true |
[[Value]](属性值) | 包含这个属性的数据值。读取属性值的时候,从这个位置读取 | undefined |
创建一个 属性 为 name,属性值 为 小明 的 student 对象
const student = { name: '小明' }
这里 name 的属性值为 小明, 也就是说 name 的 [[Value]] 特性被设置成 '小明', 而对这个值的任何修改都将反映到这个位置上
要修改属性默认的特性,只能使用 Object.defineProperty() 方法。
这个方法接收三个参数:属性所在的对象,属性名,和配置对象。
其中配置对象的属性值为:
- configurable
- enumerable
- writable
- value
将对象 student 的属性 name 的特性修改为不可删除的,可以通过 Object.defineProperty() 方法实现:
const student = { name: '小明', age: 22 } Object.defineProperty(student, 'name', { // 默认为true,当值为false时,该属性不可以被 delete 删除 configurable: false }) delete student.age console.log(student) // { name: '小明' } delete student.name // 严格模式下会抛出错误,非严格模式下会被忽略 console.log(student) // { name: '小明' }
将对象 student 的属性 sex 配置为不可被枚举的代码:
const student = { name: '小明', age: 22, sex: '男' } for (const prop in student) { console.log(prop) // name age sex } Object.defineProperty(student, 'sex', { enumerable: false }) for (const prop in student) { console.log(prop) // name age }
将对象 student 的属性 name 改为只读的:
const student = { name: '小明' } Object.defineProperty(student, 'name', { writable: false }) student.name = '小红' // 严格模式下会抛出错误,非严格模式下会被忽略 console.log(student.name) // 小明
访问器属性
访问器属性不包含数据值,它们包含一对 getter 和 setter 函数(它们不是必需的)
在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值
在写入访问器属性时,会调用 setter 函数并传入新的值,这个函数负责决定如何处理数据
访问器属性有以下4个特性:
特性 | 描述 | 默认值 |
---|---|---|
[[Configurable]](可配置的) | 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性 | true |
[[Enumerable]] | 表示能否通过 for-in 循环返回属 | true |
[[Get]] | 在读取属性时调用的函数 | undefined |
[[Set]] | 在写入属性时调用的函数 | undefined |
访问器属性的前面时下划线,表示只能通过对象方法访问的属性,下面代码的访问器属性 birthday 中包含了一对 getter 和 setter 函数。
getter 函数返回 _birthday 的值,setter 函数通过计算来确定正确的版本。因此,把 birthday 属性修改为 '1999/04/22' 会导致 _birthday 变成 '1999/04/22',而 age 变为 22.
这是使用访问器属性的常见方式,即设置一个属性的值会导致其他的属性值发生变化。
const student = { name: '小明', age: 0, _birthday: '' } Object.defineProperty(student, 'birthday', { get: function(){ return this._birthday }, set: function(val){ this._birthday = val this.age = new Date().getFullYear() - new Date(val).getFullYear() } }) student.birthday = '1999/04/22' console.log(student) // {name: "小明", age: 22, _birthday: "1999/04/22"}
不一定非要同使指定 getter 和 setter。 只指定 getter 意味着属性是不能写入的,尝试写入属性会被忽略。在严格模式下,尝试写入指定了 getter 函数的属性会抛出错误。 类似地,只指定 setter 函数的属性页不能读取,否则会抛出 undefine,在严格模式下会抛出错误。
定义多个属性
由于为对象定义多个属性的可能性很大,可以通过 Object.defineProperties() 方法来一次定义多个属性,具体代码如下:
const student = {} Object.defineProperties(student, { name: { value: '小明' // 以下被注释的代码不写则属性值默认为 false // configurable: true, // enumerable: true, // writable: true }, age: { value: 0, writable: true }, _birthday: { value: '', writable: true }, birthday: { get: function() { return this._birthday }, set: function(val) { this._birthday = val this.age = new Date().getFullYear - new Date(val).getFullYear() } } }) student.birthday = '1999/04/22' console.log(student) // {name: "小明", age: NaN, _birthday: "1999/04/22"}
读取属性的特性
想要读取属性的特性,可以通过 Object.getOwnPropertyDescriptor() 方法,获取 刚才以上的 student 属性特性的代码如下:
const descName = Object.getOwnPropertyDescriptor(student, 'name') console.log(descName) // {value: "小明", writable: false, enumerable: false, configurable: false} const descAge = Object.getOwnPropertyDescriptor(student, 'age') console.log(descAge) // {value: 22, writable: true, enumerable: false, configurable: false} const descBirth1 = Object.getOwnPropertyDescriptor(student, 'birthday') console.log(descBirth1) // {get: ƒ, set: ƒ, enumerable: false, configurable: false} const descBirth2 = Object.getOwnPropertyDescriptor(student, '_birthday') console.log(descBirth2) // {value: "1999/04/22", writable: true, enumerable: false, configurable: false}