ECMAScript 5 中定义了一个名叫“属性描述符”的对象,用于描述了的各类特征。属性描述符对象有4个属性:javascript
configurable
:可配置性,控制着其描述的属性的修改,表示可否修改属性的特性,可否把属性修改成访问器属性,或者可否经过delete
删除属性从而从新定义属性。默认值为true
。vue
enumerable
:可枚举性,表示可否经过for-in
遍历获得属性。默认值为true
。java
writable
:可写性,表示可否修改属性的值。默认值为true
。函数
value
:数据属性
,表示属性的值。默认值为undefined
。学习
除了上面的属性,还有两个存取器属性
,分别是get
和set
,能够代替value
和writable
。this
get
:在读取属性时调用的函数。只指定get
则表示属性为只读属性。默认值为undefined
。code
set
:在写入属性时调用的函数。只指定set
则表示属性为只写属性。默认值为undefined
。对象
“属性描述符”对象只能在Object.defineProperty
或Object.defineProperties
中使用。ip
API 用法
Object.defineProperty:https://developer.mozilla.org...
Object.defineProperties: https://developer.mozilla.org...get
var hello = {} Object.defineProperty(hello, 'girl', { configurable: false, enumberable: false, writable: true, value: 'sexy' }) // 存取器 Object.defineProperty(hello, 'woman', { configurable: false, enumberable: false, get: function() { return this.girl }, set: function(val) { this.girl = val } }) // 定义多个属性 Object.defineProperties(hello, { boy: { configurable: false, enumberable: false, writable: false, value: 'handsome' }, man: { configurable: false, enumberable: false, writable: true, get: function() { return this.boy } } })
当用Object.defineProperty
或Object.defineProperties
操做(新建或者修改)那些不容许建立或修改的属性时,会抛出类型错误异常。
// 此例子运行在前面的例子的基础上 Object.defineProperty(hello, 'boy', { writable: true }) // Uncaught TypeError: Cannot redefine property: boy
由于前面boy
属性已经被设置为不可配置,因此这里修改writable
会抛出类型错误异常。
经过Object.getOwnPropertyDescriptor
或者Object.getOwnPropertyDescriptors
能够获得属性描述符。
API 用法
Object.getOwnPropertyDescriptor:https://developer.mozilla.org...
Object.getOwnPropertyDescriptors:https://developer.mozilla.org...
var rules = { common: 'test' }
若是属性是不可配置的,则不能修改它的可配置性和可枚举性。
Object.defineProperty(rules, 'rule1', { configurable: false, enumberable: false }) // 修改configurable会抛出类型错误异常 Object.defineProperty(rules, 'rule1', { configurable: true }) // Uncaught TypeError: Cannot redefine property: rule1 // 修改enumberable不会抛出异常,但enmuberable没有被修改 Object.defineProperty(rules, 'rule1', { enumberable: true }) Object.getOwnPropertyDescriptor(rules, 'rule1') // Object {value: undefined, writable: false, enumerable: false, configurable: false}
若是存取器属性是不可配置的,则不能修改
get
和set
方法,也不能将它转换为数据属性。
Object.defineProperty(rules, 'rule2', { configurable: false, enumberable: false, get: function() { return this.common }, set: function(val) { this.common = val } }) // 修改get或者set方法会抛出类型错误异常 Object.defineProperty(rules, 'rule2', { get: function() { return this.common + 'rule2' } }) // Uncaught TypeError: Cannot redefine property: rule2 Object.defineProperty(rules, 'rule2', { set: function(val) { this.common = 'rule2' } }) // Uncaught TypeError: Cannot redefine property: rule2 // 将它转换为数据属性一样会抛出类型错误异常 Object.defineProperty(rules, 'rule2', { value: 'rule2' }) // Uncaught TypeError: Cannot redefine property: rule2
若是数据属性是不可配置的,则不能将它转换为存取器属性;同时,也不能将它的可写性从
false
修改成true
,但能够从true
修改成false
。
Object.defineProperty(rules, 'rule3', { configurable: false, writable: false, value: 'rule3' }) // 修改writable为true会抛出类型错误异常 Object.defineProperty(rules, 'rule3', { writable: true }) Object.defineProperty(rules, 'rule4', { configurable: false, writable: true, value: 'rule4' }) // 能够修改writable为false Object.defineProperty(rules, 'rule4', { writable: false }) Object.getOwnPropertyDescriptor(rules, 'rule4') // Object {value: "rule4", writable: false, enumerable: false, configurable: false}
若是数据属性是不可配置且不可写的,则不能修改他的值;若是是可配置但不可写,则能够修改他的值(其实是先将它标记为可写的,而后修改它的值,最后再将它标记回不可写)。
其实这里所说的修改值,是经过Object.defineProperty
或Object.defineProperties
方法修改。经过直接赋值的方法在数据属性不可配置的状况下是不能修改属性值的。
Object.defineProperty(rules, 'rule5', { configurable: false, writable: false, value: 'rule5' }) // 修改属性值会抛出类型错误异常 Object.defineProperty(rules, 'rule5', { value: 'rule55' }) // Uncaught TypeError: Cannot redefine property: rule5 rules.rule5 = 'rule55' // 值没有被修改,也不会抛出异常 rules.rule5 // 'rule5' Object.defineProperty(rules, 'rule6', { configurable: true, writable: false, value: 'rule6' }) // 修改属性值 Object.defineProperty(rules, 'rule6', { value: 'rule66' }) rules.rule6 // 'rule66' rules.rule6 = 'rule6' // 值没有被修改,也不会修改 rules.rule6 // 'rule6'
只指定
get
不能写,若是尝试对该属性赋值,会抛出类型错误异常。(红宝书上说只有在严格模式下才抛出异常)
Object.defineProperty(rules, 'rule7', { get: function() { return this.common } }) rules.rule7 = 'rule7' // Uncaught TypeError: Cannot redefine property: rule7
只指定
set
不能读,若是尝试读取该属性值,返回undefined。(红宝书上说在严格模式下才抛出异常,但没有)
Object.defineProperty(rules, 'rule8', { set: function() { this.common = 'rule8' } }) rules.rule8 // undefined
若是对象是不可扩展的,则能够编辑已有的自有属性,但不能给它添加新属性。
操做对象可扩展性的API有三个:Object.preventExtensions
、Object.seal
、Object.freeze
。
API 用法
Object.preventExtensions:https://developer.mozilla.org...
Object.seal:https://developer.mozilla.org...
Object.freeze:https://developer.mozilla.org...
Object.isExtensions:https://developer.mozilla.org...
Object.isSealed:https://developer.mozilla.org...
Object.isFrozen:https://developer.mozilla.org...
使用Object.preventExtensions
能够将对象转换为不可扩展。
使用Object.isExtensions
来判断对象是否可扩展。
var ex = {} Object.defineProperty(ex, 'ex1', { configurable: true, writable: true, value: 'ex1' }) Object.isExtensible(ex) // true Object.preventExtensions(ex) Object.isExtensible(ex) // false // 能够修改已有的属性 Object.defineProperty(ex, 'ex1', { writable: false, value: 'ex11' }) Object.getOwnPropertyDescriptor(ex, 'ex1') // Object {value: "ex11", writable: false, enumerable: false, configurable: true} // 添加属性会抛出类型错误异常 Object.defineProperty(ex, 'ex2', { value: 'ex2' }) // Uncaught TypeError: Cannot define property:ex2, object is not extensible.
使用Object.seal
除了能够将对象转换为不可扩展的,还能够将对象的全部自有属性都转换为不可配置的。即不能给对象添加新属性,并且它已有的属性也不能删除或者配置(这里一样会遵循前面的规则)。
使用Object.isSealed
来判断对象是否封闭(sealed)。
var se = {} Object.defineProperty(se, 'se1', { configurable: true, writable: false, value: 'se1' }) Object.isSealed(se) // false Object.seal(se) Object.isSealed(se) // true // 修改已有的属性会抛出类型错误异常 Object.defineProperty(se, 'se1', { writable: true, value: 'se11' }) // Uncaught TypeError: Cannot redefine property: se1 // 添加属性会抛出类型错误异常 Object.defineProperty(se, 'se2', { value: 'se2' }) // Uncaught TypeError: Cannot define property:se2, object is not extensible.
使用Object.freeze
除了将对象转换为不可扩展的和将其属性转换为不可配置的以外,还能够将自有属性转换为只读。(若是对象设置了set
,存取器属性将不会受影响,仍能够调用set
方法,并且不会抛出异常,但若是set
方法是改变该对象的属性,则不能修改为功)
使用Object.isFrozen
来检测对象是否冻结(frozen)。
var fr = {} Object.defineProperty(fr, 'fr1', { configurable: true, writable: false, value: 'fr1' }) Object.isFrozen(fr) // false Object.freeze(fr) Object.isFrozen(fr) // true // 修改已有的属性会抛出类型错误异常 Object.defineProperty(fr, 'fr1', { writable: true, value: 'fr11' }) // Uncaught TypeError: Cannot redefine property: fr1 // 添加属性会抛出类型错误异常 Object.defineProperty(fr, 'fr2', { value: 'fr2' }) // Uncaught TypeError: Cannot define property:fr2, object is not extensible. fr.fr1 = 'fr11' // 不能修fr1属性 fr.fr1 // 'fr1'
var set = {} Object.defineProperty(set, 'set1', { configurable: true, value: 'set1' }) Object.defineProperty(set, 'set2', { configurable: true, set: function(val) { this.set1 = val } }) Object.isFrozen(set) // false Object.freeze(set) Object.isFrozen(set) // true set.set2 = 'set2' set.set1 // 'set1'
我对属性描述符很不熟悉,主要是由于平时用得少。不过最近,开始学写一些小的库(虽然很挫),就感受属性描述符有使用的场景了。我暂时能想到的就是将库对象的一些属性设置为只读,以防止对象的一些属性被用户重写覆盖了。还有一个用法是在知乎和学vue的时候知道的,就是经过getter
和setter
实现“监听”对象属性的数据更新(在这里挖一个坑。后面学习一下这种方法,再写一篇“监听”对象属性的数据更新的文章)。最后,若是你们知道更多属性描述符的使用后场景,但愿你们能在评论区留下大家的高见。