对象属性的四大特性
对象的属性除了包含名字和值之外,属性还包含了写标识它们可写,可枚举和可配置的特性
ES5后,对于属性的三大特性,js提供对应的API。通过这些API给原型对象添加方法,并将它们设置成不可枚举的,这让它们看上去更像内置方法。
可以通过这些api给对象定义不能修改或删除的属性,借此封锁这个对象。
数据属性的4个特性分别是:
1.值(value)
2.可写性(writable)
3.可枚举性(enumerable)
4.可配置性(configurable)
存取器属性的4个特性
1.读取(get)
2.写入(set)
3.可枚举性(enumerable)
4.可配置性(configurable)
ES5定义了一个对象名为“属性描述符”(property descriptor)来代表对象属性的特性。数据属性的描述符对象的属性有value,writable,enumerable , configurable,存取器属性的描述符对象则用get和set属性代替value和writable。其中enumerable,configurable,writable是布尔值,set和get属性是函数值。
通过调用Object.getOwnPropertyDescriptior()可以获得某个对象特定属性的属性描述符。该API只能得到自由属性的描述符。要想获得对象集成对象属性的特性,需要遍历原型链(Object.getPrototypeOf()).若想设置属性的特性或者想让新建的属性具有某种特性,则需要调用Object.defineProperty()
Object.defineProperty(O,"x",{
value:1, writable:true, enumerable:false, configurable:true
})
属性的可枚举性和遍历
可枚举性
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }
描述对象的enumerable属性,称为”可枚举性“,如果该属性为false,就表示某些操作会忽略当前属性。
目前,有四个操作会忽略enumerable为false的属性。
for...in循环:只遍历对象自身的和继承的可枚举的属性。Object.keys():返回对象自身的所有可枚举的属性的键名。JSON.stringify():只串行化对象自身的可枚举的属性。Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
这四个操作之中,前三个是 ES5 就有的,最后一个Object.assign()是 ES6 新增的。其中,只有for...in会返回继承的属性,其他三个方法都会忽略继承的属性,只处理对象自身的属性。实际上,引入“可枚举”(enumerable)这个概念的最初目的,就是让某些属性可以规避掉for...in操作,不然所有内部属性和方法都会被遍历到。比如,对象原型的toString方法,以及数组的length属性,就通过“可枚举性”,从而避免被for...in遍历到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
Object.getOwnPropertyDescriptor([], 'length').enumerable
// false
上面代码中,toString和length属性的enumerable都是false,因此for...in不会遍历到这两个继承自原型的属性。
另外,ES6 规定,所有 Class 的原型的方法都是不可枚举的。
Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false
总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in循环,而用Object.keys()代替。