在ECMAScript中属性分两种类型:数据属性和访问属性
1.数据属性
数据属性有四个描述其行为的特性个:[[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]]。
- [[Congigurable]]表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否将数据属性改为访问属性,默认值为true
- [[Enumerable]]表示属性是否可枚举,即使用for in 返回属性,默认值为true
- [[Writable]]表示是否能修改属性值,默认值为true
- [[Value]]表示包含属性的数据值,读取属性时,从这个位置读取属性,写入新的属性时将新的属性保存在这个位置,默认值是undefined
let obj={
name:"JSindexHTML"
};
在上面的代码中,我们定义了一个对象obj,属性名为name,属性值为JSindexHTML。在这个对象的数据属性 [[Value]] 值就被设置为 JSindexHTML。对属性 name 的任何修改都会相应的改变这个对象的数据属性 [[Value]] 的值。
如果要修改数据属性的特性,我们可以使用 Object.defineProperty() 方法。这个方法接收三个参数:属性所在的对象,属性名(属性名需要加引号),描述符对象。其中描述符对象的属性必须是:configurable、enumerable、writable 和 value且为小写。
Object.defineProperty(obj,"name",{
writable:false,
value:"JSindexHTML"
})
obj.name="Jack";
console.log(obj.name);//JSindexHTML
在上面的代码中,我们将对象的数据属性 [[Writable]]修改为 false,即禁止修改对象的属性值。此时name 属性是只读的,修改name值,严格模式下会抛出错误,非严格模式忽略。所以当我们将对象obj的name属性值修改为Jack时,console.log(obj.name) 的结果是 JSindexHTML。
如果我们把[[Writable]]修改为 true:
Object.defineProperty(obj,"name",{
writable:true,
value:"JSindexHTML"
})
obj.name="Jack";
console.log(obj.name);//Jack
当我们将 writable更改为true后,更改 obj 的 name 属性值:obj.name="Jack",console.log(obj.name)的结果为修改后的 name 的属性值 Jack。
上面的规则也适用于配置属性:
Object.defineProperty(obj,"name",{
configurable:false,
value:"JSindexHTML"
})
console.log(obj.name);//Jack
在上面的代码中,我们对象的数据属性 configurable 设置为false,所以不使用 delete 删除我们的对象属性。所以console.log(obj.name)的结果为 JSindexHTML。
在调用 Object.defineProperty()方法时,如果不指定,configurable、enumerable 和 writable 特性的默认值都是 false。
2.访问属性
访问属性不包含数据值而是包含一对儿 get和 set函数,但这两个函数不是必须的。在读取访问属性时会调用getter函数,这个函数负责返回有效值;在写入访问属性时,会调用setter函数并传入新值,这个函数决定如何处理数据。访问属性有以下四个特性:
访问属性不能直接定义,必须使用Object.definedProperty()方法定义。
let book={
_year:2019,
edition:1
}
Object.defineProperty(book,"year",{
get:function(){
return this._year
},
set:function(newValue){
if(newValue>2019){
this._year=newValue;
this.edition+=newValue-2019;
}
}
}
)
book.year=2020;
console.log(book.edition);//2
在上面的代码中,我们定义了 book 对象,并给这个对象添加了两个属性 _year 和 edition。_year前面的下划线是一种常用几号,用于表示只能通过对象方法访问的属性。访问器属性year包含了两个属性setter 和 getter 函数,getter函数获取_year 的值,而setter函数通过计算确定正确的版本。因此当我们把year属性修改为2020的时候,_year就会变成2019,而 edition 变为 2。这是使用访问器属性常见的方式,即设置一个属性的值也会导致其他属性值发生变化。
为了兼容旧版本的浏览器,我们可以使用两个非标准方法创建访问属性器:__defineGetter__ 和 __defineSetter__。注意,这两个方法的前后的下划线是分别是由两条组成。
let book={
_year:2019,
edition:1
}
book.defineGettet=function(){
return this._year;
}
book.defineSetter=function(newValue){
if(newValue>2019){
this._year=newValue;
this.edition+=newValue-2019;
}
}
book.year=2020;
console.log(book.edition);//2
在不支持Object.defineProperty()方法的浏览器中不能修改[[Confirguable]] 和 [[Enumerable]].