JS屬性特性(屬性描述子)
ECMAScript 5 中定義了一個名叫「屬性描述子」的對象,用來描述了的各種特徵。 「屬性描述子」物件只能在Object.defineProperty或Object.defineProperties中使用。
概念
ECMAScript 5 中定義了一個名叫「屬性描述符」的對象,用於描述了的各種特徵。屬性描述符物件有4個屬性:
configurable:可配置性,控制其描述的屬性的修改,表示能否修改屬性的特性,能否把屬性修改為存取器屬性,或能否透過delete刪除屬性從而重新定義屬性。預設值為true。
enumerable:可枚舉性,表示能否透過for-in遍歷得到屬性。預設值為true。
writable:可寫性,表示能否修改屬性的值。預設值為true。
value:資料屬性,表示屬性的值。預設值為undefined。
除了上面的屬性,還有兩個存取器屬性,分別是get和set,可以取代value和writable。
get:在讀取屬性時呼叫的函數。只指定get則表示屬性為唯讀屬性。預設值為undefined。
set:在寫入屬性時呼叫的函數。只指定set則表示屬性為只寫屬性。預設值為undefined。
使用
「屬性描述子」物件只能在Object.defineProperty或Object.defineProperties中使用。
API 用法
Object.defineProperty:https://developer.mozilla.org...
Object.defineProperties: https://developer.mozilla.org...
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(hello, 'boy', { writable: true }) // Uncaught TypeError: Cannot redefine property: boy當用Object.def://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}.defineProperties操作(新建或修改)那些不允許建立或修改的屬性時,會拋出類型錯誤異常。
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因為前面boy屬性已經被設定為不可配置,所以這裡修改writable會拋出類型錯誤異常。 透過Object.getOwnPropertyDescriptor或Object.getOwnPropertyDescriptors可以得到屬性描述符。 API 用法Object.getOwnPropertyDscriptor:https://developer.mozilla.org...Object.getOwnPropertyDescriptors:https://developer.mozilla.org...規則
配置的,則不能修改它的可配置性和可枚舉性。
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}
如果存取器屬性是不可設定的,則不能修改get和set方法,也不能將它轉換為資料屬性。
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'
如果資料屬性是不可配置的,則不能將它轉換為存取器屬性;同時,也不能將它的可寫性從false修改為true,但可以從true修改為false。
Object.defineProperty(rules, 'rule7', { get: function() { return this.common } }) rules.rule7 = 'rule7' // Uncaught TypeError: Cannot redefine property: rule7
如果資料屬性是不可配置且不可寫的,則不能修改他的值;如果是可配置但不可寫,則可以修改他的值(實際上是先將它標記為可寫的,然後修改它的值,最後再將它標記回不可寫)。
其實這裡所說的修改值,是透過Object.defineProperty或Object.defineProperties方法修改。透過直接賦值的方法在資料屬性不可配置的情況下是不能修改屬性值的。
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.
只指定set不能讀,如果嘗試讀取該屬性值,回傳undefined。 (紅寶書上說在嚴格模式下才拋出異常,但沒有)
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.
如果物件是不可擴展的,則可以編輯已有的自有屬性,但不能給它添加新屬性。
操作物件可擴充性的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.freeze:https://developer.mozilla.org...
Object.freeze:https://developer.mozilla.org...
Object.freeze:https://developer.mozilla.org...
Object.freeze:https://developer.mozilla.org...
Object。 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 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'🎜使用Object.seal除了可以將物件轉換為不可擴充的,還可以將物件的所有自有屬性都轉換為不可配置的。即不能為物件新增屬性,而且它已有的屬性也不能刪除或配置(這裡同樣會遵循前面的規則)。 🎜🎜使用Object.isSealed來判斷物件是否封閉(sealed)。 🎜rrreee🎜使用Object.freeze除了將物件轉換為不可擴展的和將其屬性轉換為不可配置的之外,還可以將自有屬性轉換為唯讀。 (如果物件設定了set,存取器屬性將不會受影響,仍可以呼叫set方法,而且不會拋出異常,但如果set方法是改變該物件的屬性,則不能修改成功)🎜🎜使用Object .isFrozen來偵測物件是否凍結(frozen)。 🎜rrreee🎜結語🎜
我對屬性描述符很不熟悉,主要是因為平常用得少。不過最近,開始學寫一些小的庫(雖然很挫),就感覺屬性描述符有使用的場景了。我暫時能想到的就是將庫物件的一些屬性設為唯讀,以防止物件的一些屬性被使用者重寫覆蓋了。還有一個用法是在知乎和學vue的時候知道的,就是透過getter和setter實現「監聽」物件屬性的資料更新