数据属性和访问器属性

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() 方法时,如果不指定,configurableenumerablewritable 特性的默认值都是 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”

因为 objname 属性的 writable 被设为 false,因此这个属性的值是不可修改的,如果尝试为它指定新值,则在非严格模式下,赋值操作将被忽略;在严格模式下,赋值操作将会导致抛出错误。

for(var i in obj){
	console.log(i);			
}
//输出“age”

因为 objname 属性的 enumerable 被设为 false,因此这个属性不能通过 for-in 循环返回。

delete obj.name;
console.log(obj.name);		//输出“XD”

因为 objname 属性的 configurable 被设为 false,因此这个属性的值是不可删除的,如果对这个属性调用 delete,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。而且,一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty() 方法修改特性,除了将 writabletrue 改为 false,其他都会导致错误,具体可以查看关于数据属性特性configurable设置为false后的限制。

访问器属性

访问器属性有如下 4 个特性:
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true
[[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为 true
[[Get]]:在读取属性时调用的函数。默认值为 undefined
[[Set]]:在写入属性时调用的函数。默认值为 undefined

访问器属性不能直接定义,必须使用 Object.defineProperty() 来定义。访问器属性不包含数据值,包含的是一对getset方法,在读写访问器属性时,就是通过这两个方法来进行操作处理的。

var obj = {
	_name : "Kobe"
};

上面代码定义了一个对象objobj里面有一个_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”

因为 objname 访问器属性的 enumerable 被设为 false,因此这个属性不能通过 for-in 循环返回。

delete obj.name;
console.log(obj.name);		//输出“His name is James Bryant”

因为 objname 访问器属性的 configurable 被设为 false,因此这个属性不能被删除。并且不能修改属性的特性,也不能把属性修改为数据属性。

不一定非要同时指定 getset。只指定 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() 方法时,如果不指定,configurableenumerable 特性的默认值都是 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() 方法,可以取得给定属性的描述符。这个方法接收两个参数:属性所在的对象要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurableenumerablegetset;如果是数据属性,这个对象的属性有 configurableenumerablewritablevalue

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";
		}
	}
});

你可能感兴趣的:(JavaScript,数据属性,访问器属性,前端,对象属性)