一。对象的属性描述
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对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的。