JavaScript里面的对象是由属性和方法构成的。
var obj = {
name : "XD", //属性
sayName : function(){ //方法
alert("My name is " + this.name);
}
};
而对象的属性有两种类型,一种是数据属性,一种是访问器属性。
属性在创建时都带有一些特征值,JavaScript 通过这些特征值来定义它们的行为。在 JavaScript 中不能直接访问这些特征值。为了表示特性是内部值,该规范把它们放在了两对方括号中,例如[[Enumerable]]
。
数据属性有 4 个描述其行为的特性:
[[Configurable]]
:表示能否通过 delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true
。
[[Enumerable]]
:表示能否通过 for-in
循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true
。
[[Writable]]
:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true
。
[[Value]]
:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined
。
var obj = {
name : "XD", //属性
};
像上面例子中直接在对象上定义的属性,默认为数据属性,它们的[[Configurable]]
、[[Enumerable]]
和[[Writable]]
特性都被设置为 true
,而[[Value]]
特性被设置为指定的值。
要修改属性默认的特性,必须使用 Object.defineProperty()
方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是数据属性特性或访问器属性特性其中的一或多个值。
在调用 Object.defineProperty()
方法时,如果不指定,configurable
、enumerable
和writable
特性的默认值都是 false
,不指定 value
的话值为 undefined
。
var obj = {
age : 23
};
Object.defineProperty(obj , "name" , {
writable: false,
value: "XD",
enumerable: false,
configurable: false
});
obj.name = "JH";
console.log(obj.name); //输出“XD”
因为 obj
的 name
属性的 writable
被设为 false
,因此这个属性的值是不可修改的,如果尝试为它指定新值,则在非严格模式下,赋值操作将被忽略;在严格模式下,赋值操作将会导致抛出错误。
for(var i in obj){
console.log(i);
}
//输出“age”
因为 obj
的 name
属性的 enumerable
被设为 false
,因此这个属性不能通过 for-in
循环返回。
delete obj.name;
console.log(obj.name); //输出“XD”
因为 obj
的 name
属性的 configurable
被设为 false
,因此这个属性的值是不可删除的,如果对这个属性调用 delete
,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。而且,一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty()
方法修改特性,除了将 writable
从 true
改为 false
,其他都会导致错误,具体可以查看关于数据属性特性configurable设置为false后的限制。
访问器属性有如下 4 个特性:
[[Configurable]]
:表示能否通过 delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true
。
[[Enumerable]]
:表示能否通过 for-in
循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为 true
。
[[Get]]
:在读取属性时调用的函数。默认值为 undefined
。
[[Set]]
:在写入属性时调用的函数。默认值为 undefined
。
访问器属性不能直接定义,必须使用 Object.defineProperty()
来定义。访问器属性不包含数据值,包含的是一对get
和set
方法,在读写访问器属性时,就是通过这两个方法来进行操作处理的。
var obj = {
_name : "Kobe"
};
上面代码定义了一个对象obj
,obj
里面有一个_name
属性。_name
属性前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。
Object.defineProperty(obj , "name" , {
enumerable: false,
configurable: false,
get: function(){
return "His name is " + this._name;
},
set: function(value){
this._name = value + " Bryant";
}
});
上面代码定义了obj
的一个访问器属性name
,到这里,obj
一共有两个属性,一个是数据属性_name
,另一个是访问器属性name
。
console.log(obj._name); //输出“Kobe”
console.log(obj.name); //读取访问器属性 name 会调用 get 函数,所以输出“His name is Kobe”
obj.name = "James"; //设置 obj 的访问器属性 name 的值会调用 set 函数,导致 _name 的值被设为 "James Bryant"
console.log(obj._name); //输出“James Bryant”
console.log(obj.name); //读取访问器属性 name 会调用 get 函数,所以输出“His name is James Bryant”
访问器属性 name
则包含一个 get
函数和一个 set
函数。get
函数返回字符串"His name is "
与 _name
属性的值的拼串,set
函数将 _name
属性的值设置为 value
与 "Bryant"
的拼串,value
为修改属性值时传入的值。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
for(var i in obj){
console.log(i);
}
//输出“_name”
因为 obj
的 name
访问器属性的 enumerable
被设为 false
,因此这个属性不能通过 for-in
循环返回。
delete obj.name;
console.log(obj.name); //输出“His name is James Bryant”
因为 obj
的 name
访问器属性的 configurable
被设为 false
,因此这个属性不能被删除。并且不能修改属性的特性,也不能把属性修改为数据属性。
不一定非要同时指定 get
和 set
。只指定 get
意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了 get
函数的属性会抛出错误。
var obj = {
_name : "Kobe"
};
Object.defineProperty(obj , "name" , {
enumerable: false,
configurable: false,
get: function(){
return "His name is " + this._name;
}
});
obj.name = "James";
console.log(obj._name); //输出“Kobe”
console.log(obj.name); //读取访问器属性 name 会调用 get 函数,所以输出“His name is Kobe”
类似地,只指定 set
函数的属性也不能读,否则在非严格模式下会返回 undefined
,而在严格模式下会抛出错误。
var obj = {
_name : "Kobe"
};
Object.defineProperty(obj , "name" , {
enumerable: false,
configurable: false,
set: function(value){
this._name = value + " Bryant";
}
});
obj.name = "James";
console.log(obj._name); //输出“James Bryant”
console.log(obj.name); //读取访问器属性 name 会调用 get 函数,因为 name 没有 get 函数,所以输出undefined
在调用 Object.defineProperty()
方法时,如果不指定,configurable
、enumerable
特性的默认值都是 false
。
var obj = {
_name : "Kobe"
};
Object.defineProperty(obj , "name" , {
get: function(){
return "His name is " + this._name;
},
set: function(value){
this._name = value + " Bryant";
}
});
for(var i in obj){
console.log(i); //输出“_name”,name属性没有被循环返回
}
delete obj.name;
console.log(obj.name); //输出“His name is James Kobe”,name属性没有被删除
使用 Object.getOwnPropertyDescriptor()
方法,可以取得给定属性的描述符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable
、enumerable
、get
和 set
;如果是数据属性,这个对象的属性有 configurable
、enumerable
、writable
和 value
。
var obj = {};
Object.defineProperty(obj , "name" , {
writable: false,
value: "XD",
enumerable: false,
configurable: false
});
console.log(Object.getOwnPropertyDescriptor(obj , "name"));
//输出 {value: "XD", writable: false, enumerable: false, configurable: false}
var obj = {
_name : "Kobe"
};
Object.defineProperty(obj , "name" , {
enumerable: false,
configurable: false,
get: function(){
return "His name is " + this._name;
},
set: function(value){
this._name = value + " Bryant";
}
});
console.log(Object.getOwnPropertyDescriptor(obj , "name"));
//输出 {get: ƒ, set: ƒ, enumerable: false, configurable: false}
使用 Object.defineProperties()
方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。
var obj = {
_name : "Kobe"
};
Object.defineProperties(obj ,{
age : {
writable: false,
value: 40,
enumerable: false,
configurable: false
},
name : {
enumerable: false,
configurable: false,
get: function(){
return "His name is " + this._name;
},
set: function(value){
this._name = value + " Bryant";
}
}
});