一。對象的屬性描述
JavaScript “屬性描述對象”(attributes object)
{
value: 屬性的屬性值
writable:是否可寫
enumerable:是否可遍歷 比如for...in循環、Object.keys())跳過該屬性
configurable: 可配置性,控制了屬性描述對象的可寫性
get: undefined,取值函數(getter),默認為undefined
set: undefined,存值函數(setter),默認為undefined。
}
二。獲取屬性
1. Object.getOwnPropertyDescriptor():獲取指定屬性的描述值(只能用於對象自身的屬性,不能用於繼承的屬性)
參數:第一個參數是目標對象,第二個參數是一個字符串(對應目標對象的某個屬性名),效果如下:
2. Object.getOwnPropertyNames():方法返回一個數組,成員是參數對象自身的全部屬性的屬性名,不管該屬性是否可遍歷。【Object.keys
只返回對象自身的可遍歷屬性的全部屬性名】
3. Object.defineProperty():允許通過屬性描述對象,定義或修改一個屬性,然后返回修改后的對象。
定義和修改🌰:如下定義一個對象obj,有一個p屬性,定義p屬性的writable為false,則可以看到修改p值后,並沒有生效。【其次,當定義一個存在的屬性時,即是修改了】
如果一次性定義或修改多個屬性,可以使用Object.defineProperties()
方法:如下
var obj = Object.defineProperties({}, { p1: { value: 123, enumerable: true }, p2: { value: 'abc', enumerable: true }, p3: { get: function () { return this.p1 + this.p2 }, enumerable:true, configurable:true } }); obj.p1 // 123 obj.p2 // "abc" obj.p3 // "123abc"
4. Object.prototype.propertyIsEnumerable():返回一個布爾值,用來判斷某個屬性是否可遍歷。注意,這個方法只能用於判斷對象自身的屬性,對於繼承的屬性一律返回false
。
5. 對象的拷貝:hasOwnProperty
那一行用來過濾掉繼承的屬性,否則可能會報錯,因為Object.getOwnPropertyDescriptor
讀不到繼承屬性的屬性描述對象。
var extend = function (to, from) { for (var property in from) { if (!from.hasOwnProperty(property)) continue; Object.defineProperty( to, property, Object.getOwnPropertyDescriptor(from, property) ); } return to; } extend({}, { get a(){ return 1 } }) // { get a(){ return 1 } })
6. 對象凍結(控制對象狀態):凍結對象的讀寫狀態,防止對象被改變。
方案一:最強的Object.freeze。【無法添加新屬性、無法刪除舊屬性、也無法改變屬性的值,使得這個對象實際上變成了常量】
對應檢查方法:Object.isFrozen
var obj = { p: 'hello' }; Object.freeze(obj); obj.p = 'world'; obj.p // "hello" obj.t = 'hello'; obj.t // undefined delete obj.p // false obj.p // "hello"
方案二:其次的Object.seal。【無法添加新屬性,也無法刪除舊屬性(可修改)】【實質是把屬性描述對象的configurable
屬性設為false
】
對應檢查方法:Object.isSealed
var obj = { p: 'a' }; // seal方法之前 Object.getOwnPropertyDescriptor(obj, 'p') // Object { // value: "a", // writable: true, // enumerable: true, // configurable: true // } Object.seal(obj); // seal方法之后 Object.getOwnPropertyDescriptor(obj, 'p') // Object { // value: "a", // writable: true, // enumerable: true, // configurable: false // } Object.defineProperty(o, 'p', { enumerable: false }) // TypeError: Cannot redefine property: p
方案三:最弱的Object.preventExtensions。【無法再添加新的屬性】
對應檢查方法:Object.preventExtensions
var obj = new Object(); Object.preventExtensions(obj); Object.defineProperty(obj, 'p', { value: 'hello' }); // TypeError: Cannot define property:p, object is not extensible. obj.p = 1; obj.p // undefined
上面的三個方法鎖定對象的可寫性有一個漏洞:
1. 可以通過改變原型對象,來為對象增加屬性。【除非把obj的原型也凍結住】
2. 如果屬性值是對象,上面這些方法只能凍結屬性指向的對象,而不能凍結對象本身的內容。如下:
var obj = { foo: 1, bar: ['a', 'b'] }; Object.freeze(obj); obj.bar.push('c'); obj.bar // ["a", "b", "c"] 上面代碼中,obj.bar屬性指向一個數組,obj對象被凍結以后,這個指向無法改變,即無法指向其他值,但是所指向的數組是可以改變的。