属性类型
ECMA-262第5版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262定义这些特性是为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们。为了表示特性是内部值,该规范把它们放在两对儿方括号中,例如[[Enumerable]]。
ECMAScript中有两种属性:数据属性和访问器属性。
数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。
**
[[Configurable]]
- 表示能否通过
delete删除属性从而重新定义属性(可配置的) - 能否修改属性的特性
- 能否把属性修改为访问器属性。
- 默认值为
true。
[[Enumerable]]
- 表示能否通过
for-in循环遍历属性(可枚举的) - 默认值为
true
[[Writable]]
- 表示能否修改属性的值。(可写的)
- 默认值为
true
[[Value]]
- 包含这个属性的数据值。
- 读取属性值的时候,从这个位置读;
- 写入属性值的时候,把新值保存在这个位置。
- 默认值为
undefined
总结一下就是:
1.configurable属性是配置属性,决定了能不能删除(delete)属性,能不能修改属性的特性。所以这个属性最好保持为true,变为false的话还无法将其改回true。2.enumberable和writable从字面意思就可以看出作用,一个枚举一个修改。3.value就是属性的值
Object.defineProperty()方法
要修改属性的默认的特性,必须使用ES5的这个方法。这个方法接收三个参数:对象,属性名和一个描述符对象。
其中描述符对象的属性就是以上介绍的四个数据属性。例如:
var person = {};Object.defineProperty(person,"name",{writable:false,value:"Nicholas"});console.log(person.name); // "Nicholas"person.name = "Greg";console.log(person.name); // "Nicholas"
分析:由于我们将person对象的name属性的writable属性设为false,所以表示这个name属性无法修改,后面尝试修改的话就会失败的。
类似的规则也适用与不可配制的属性。例如:
var person = {};Object.defineProperty(person,"name",{configurable:false,value:"Nicholas"});console.log(person.name); // "Nicholas"delete person.name;console.log(person.name); // "Nicholas"
分析:把configurable设置为false,表示不能从对象中删除属性。如果对这个属性调用delete,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。
而且,一旦把属性定义为不可配置的(false),就不能再把它变回可配置了。此时,再调用Object.defineProperty()方法修改除writable之外的特性,都会导致错误:
var person = {};Object.defineProperty(person,"name",{configurable:false,value:"Nicholas"});// 抛出错误 "Uncaught TypeError: Cannot redefine property: name"Object.defineProperty(person,"name",{configurable:true,value:"Nicholas"});
调用Object.defineProperty()方法时,如果不显式指定,configurable、enumerable和writable特性的默认值都是false。
访问器属性
访问器属性不包含数据值;它们包含一对儿getter和setter函数(不过,这两个函数都不是必需的)。
- 在读取访问器属时,会调用
getter函数,这个函数负责返回有效的值; - 在写入访问器属性时,会调用
setter函数并传入新值,这个函数负责决定如何处理数据。
访问器的四个属性
访问器属性不能直接定义,必须使用Object.defineProperty()来定义。
var book = {_year:2004,edition:1};Object.defineProperty(book,"year",{get:function () {return this._year;},set:function (newValue) {if ((newValue > 2004)) {this._year = newValue;this.edition += newValue - 2004;}}})book.year = 2005;alert(book.edition); // 2
创建一个book对象,并给它定义两个默认的属性:_year、edition。_year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性year则包含一个getter函数和一个setter函数。
get和set方法
不一定非要同时指定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了getter函数的的属性会抛出错误。类似地,没有指定setter函数的属性也不能读,否则在非严格模式下会返回undefined,在严格模式下会抛出错误。
// 下划线表示只能通过对象方法访问的属性var obj = {_x:"obj._x",_y:"obj._y",_z:"obj._z"};//Object.defineProperty(obj,"x",{ // x属性,只读不能写get:function () {return this._x;}});console.log(obj.x); // "obj._x" 可以读取,调用obj.x实际上调用了obj._x的getter函数obj.x = "Rewrite x"; // 尝试修改x属性console.log(obj.x); // "obj._x" 写入失败//Object.defineProperty(obj,"y",{ // y属性,只写不能读set:function (newValue) {this._y = newValue;}});console.log(obj.y); // "undefined" 读取失败obj.y = "Rewrite obj.y"; // 尝试修改属性console.log(obj._y); // "Rewrite obj.y" 可以写入//Object.defineProperty(obj,"z",{ // z属性可读可写get:function () {return this._z;},set:function (newValue) {this._z = newValue;}});console.log(obj.z); // "obj._z"obj.z = "Rewrite obj.z"; // 修改z属性console.log(obj._z); // "Rewrite obj._z"
定义多个属性
Object.defineProperties()
此方法用来定义多个属性。这个方法接收两个对象参数:第一个参数是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。例如:
var book = {};Object.defineProperties(book,{_year:{writable:true,value:2004},edition:{writable:true,value:1},year:{get:function () {return this._year;},set:function (newValue) {if (newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}},});
读取属性的特性
Object.getOwnPropertyDescriptor()
使用ES5的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符,这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,
如果是访问器属性,这个对象的属性有configurable、enumerable、get和set;
如果是数据属性,这个对象的属性有configurable、enumerable、writable、value。
var book = {};Object.defineProperties(book,{_year:{value:2004},edition:{value:1},year:{get:function () {return this._year;},set:function (newValue) {if (newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}}});var descriptor = Object.getOwnPropertyDescriptor(book,"_year");alert(descriptor.value); // 2004alert(descriptor.configurable); // falsealert(typeof descriptor.get); // "undefined"var descriptor = Object.getOwnPropertyDescriptor(book,"year");alert(descriptor.value); // "undefined"alert(descriptor.enumerable); // falsealert(descriptor.get); // "function"
