属性类型
说到属性类型,指的是一个对象所拥有的所有属性的特征。包括两类:数据属性和访问器属性。
属性类型的存在就是为了描述对象属性的各种特性,当初是为了实现js引擎用的,因此在js中不能直接访问,也可以理解为它是一个隐式的,例如:[[prototype]]
数据属性
数据属性包含有一个数据值的位置。在这个位置可以进行数据的读写。数据属性有4个描述其行为的特性。
[[Configurable]]
:表示能否通过delete
删除属性从而重新定义属性,也包括能否修改属性的特性,或者能否把属性修改为访问器属性。默认值为true
[[Enurmerable]]
:表示能否通过for-in
循环返回属性,说白了是否能遍历,或者可枚举。默认值为true
[[Writable]]
:表示能否修改属性的值,例如通过Object.defineProperty()
方法修改属性的值。默认值为true
[[Value]]
:包含这个属性的数据值。读取属性值的时候,从这个位置读。写入属性值的时候,把新的值保存在这个位置。默认为undefined
例如:
let person = {
name: 'zhd',
age: 18,
sex: 'boy'
}
上面我定义了一个名为person的变量,并且添加了三个对应的属性,并指定对应的值。也就是说三个属性对应的个子的[[Value]]
这个特性就被赋予zhd
,18
,boy
这三个值,以后对每个值得修改都会体现在这个位置上。
但是,如果要修改属性默认的特性,必须使用Object.defineProperty()
方法。
Object.defineProperty()
:此方法接受三个参数,第一个就是要修改的属性所在的对象,第二个是要修改属性名,第三个是描述符对象。这里需要注意一点:描述符对象的属性必须是configurable
,enumerable
,writable
,value
这四个。设置其中一个或多个值,可以修改对应的特性值。
例如:
var person = {}
Object.defineProperty(person, 'name', {
writable: false,
value: 'zhd'
})
console.log(person.name) // zhd
person.name = 'zy'
console.log(person.name) // zhd
如上面例子所示,修改了writable
特性值为false
,也就是不可写或者说不可更改,意思是一旦我设定了value
特性,并且将其writable
设置为false
,后续的所有更改name
属性值得行为都将不起作用。
这个规则同样适用configurable
,例如:
var person = {}
Object.defineProperty(person, 'name', {
configurable: false,
value: 'zhd'
})
console.log(person.name) // zhd
person.name = 'zy'
console.log(person.name) // zhd
将configurable
设置为false
,说明不能用delete
删除属性。一旦把属性定义为不可配置的,就不能把它变回可配置的。此时,如果再次调用Object.defineProperty()
方法修改除writable
外的所有属性,都会导致错误。
如下图所示:
而对于
enumerable
,和writable
这两个特性来说,就好理解了,直接看定义就好了。
访问器属性
访问器属性不包含数据值;它们包含一对getter
和setter
函数。在读取访问器属性是,会直接调用getter
函数,也就是说getter
函数就相当于访问的属性值,这个函数负责返回有效的值。在写入访问器属性时,会调用setter
函数并传入新值,这个函数负责决定如何处理数据。同样的,访问器属性除了同数据属性一样,有[[Configurable]]
和[[Enumerable]]
这两个特性外,还有两个特性:
[[Get]]
:在读取属性时调用的函数。默认值为undefined
[[Set]]
:在写入属性时调用的函数。默认值为undefined
不过,访问器属性不能直接定义,必须使用Object.defineProperty()方法来定义。
var person = {
year: 4,
edition: 1
}
Object.defineProperty(person, 'year', {
get: function() {
return this.year
},
set: function(newValue) {
if (newValue > 4) {
this.year = newValue
this.edition += newValue - 4
}
}
})
person.year = 5
alert(person.edition) // 2
如上例子所示:每次访问year
属性值,都会调用year
属性本身的一个get
函数,这个函数负责把year
属性值返回出去。而如果要修改year
属性值,则会调用year
属性本身的一个set
函数,而修改的值则会当做参数传入这个set
函数,然后在set
函数内部,做相应的逻辑处理。
不过由于标准问题,FireFox也有一套对应的方法,defineGetter()和defineSetter()。用这两个方法重写上面的例子可以这样写:
let person = {
year: 4,
edition: 1
}
person._defineGetter_('year', function() {
return this.year
})
person._defineSetter_('year', function(newValue) {
if(newValue > 4) {
this.year = newValue
this.edition += newValue - 4
}
})
定义多个属性
可以用Object.defineProperties()
方法定义多个属性。此方法接收两个参数,第一个是要添加或修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应,也是一个描述符对象。
例如:
let person = {}
Object.defineProperties(person, {
year: {
writable: true,
value: 4
},
edition: {
writable: true,
value: 1
},
_year: {
get: function() {
return this.year
},
set: function(newValue) {
if(newValue > 4) {
this._yaer = newValue
this.edition += newValue - 4
}
}
}
})
读取属性的特性
使用Object.getOwnPropertyDescriptor()
方法可以读取属性对应的特性。此方法接收两个参数。第一个是属性所在的对象,第二个是要读取其描述符的属性名称。返回值是一个对象,如果是数据属性,则返回的对象包括configurable
,enumerable
,writable
和value
这四个属性。如果是访问器属性,则返回的对象包括configurable
,enumerable
,get
和set
这四个属性。
如下例:
let book = {}
Object.getOwnPropertyDescriptor(book, {
year: {
value: 4
},
edition: {
value: 1
},
_year: {
get: function() {
return this.year
},
set: function(newValue) {
if(newValue > 4) {
this.year = newValue
this.edition += newValue - 4
}
}
}
})
let obj = Object.getOwnPropertyDescriptor(person, 'year')
console.log(obj.value) // 4
console.log(obj.configurable) // false