前言
创建一个对象有两种方式:使用构造函数,或使用对象字面量。实际上在使用对象字面量创建一个对象时不会用到构造函数。对象都有属性和方法,用来描述这个对象。而描述属性的各种特征的“属性中的属性”我们称之为“特性”。
正文
属性类型
属性的特性是用来实现Javascript引擎用的,不能直接使用。我们把特性用两个方括号
[[]]
包起来。Javascript中有两种属性:数据属性和访问器属性。
数据属性
数据属性保存了一个数据值的位置,可以读取和写入。它有4个特性:
- [[configurable]]:表示这个属性能否被
delete
操作符删除,以及能否修改这个属性的其它特性,相当于一个“总开关”。需要注意,一旦这个特性的值设置为false,就不能再设置为true了。 - [[ennumerable]]:表示这个属性能否被
for-in
循环枚举。 - [[writable]]:表示这个属性的值能否被改写。
- [[value]]:这个位置储存了这个属性的值,读取和写入都在这个位置进行。默认值为undefined.
注意,一个对象的某个属性如果是用文章开头所说的两种方式(构造函数和字面量)定义的,则这个属性的前三个特性的值都为true,最后一个特性为所定义的值。要访问和改写一个属性的特性,需要用到
Object.defineProperty(obj,propertyName,descriptor)
方法。参数解释如下:
- obj:需要访问或修改属性的特性的对象。
- propertyName:需要访问或修改其特性的属性名,注意是字符串。
- descriptor:一个描述符对象,这个对象的属性就是上述的4个特性中的某些或全部。
1 | var person = {name : 'nikolaus'}; |
在调用
Object.defineProperty()
方法时,如果不为前3个特性指定值,则其值会被设置为false。从上面的例子也可以看出。
访问器属性
访问器属性不包含数据值(这里不包含数据值是什么意思下面再讲),它总是跟对象的”私有属性”关联,用来读取和写入这些”私有属性”。访问器属性必须通过
Object.defineProperty()
方法来定义。它也有4个特性:
- [[configurable]]:表示这个属性能否被
delete
操作符删除,以及能否修改这个属性的其它特性,相当于一个“总开关”。需要注意,一旦这个特性的值设置为false,就不能再设置为true了。 - [[ennumerable]]:表示这个属性能否被
for-in
循环枚举。 - [[Get]]:读取属性时调用的函数。这个函数不是必须的。
- [[Set]]:写入属性时调用的函数。这个函数不是必须的。
1 | var person = { |
上面的实例定义了一个对象,并为对象添加了两个”数据属性”,并用
Object.defineProperty()
方法定义了两个访问器属性name
和age
。其中name
属性只为其定义了get函数,age
属性则为其定义了get和set函数。我之前一直不理解“访问器属性不包含数据值”这句话是什么意思,上面的实例中person.age = 25;
不是一个赋值操作嘛?实际上,可以发现:name
和age
这两个属性都是跟_name
和_age
关联的,可以理解为这两个访问器属性是为这两个”数据属性”服务的。看起来像是赋值操作的那个语句,实际上是通过get函数把这个值保存在了_age
这个属性上,age
这个访问器属性上并不保存数据,它只是一个中间的桥梁,通过函数执行数据的读取和写入操作。通过上面的实例发现:对于一个访问器属性,如果只给他设置get函数,则只能读取对应的属性,而不能执行写入操作;如果只给他设置set函数,则只能写入对应的属性,不能读取。
定义多个属性
Object.defineProperties(obj,descriptor)
方法可以为对象一次定义多个属性。第一个参数为将要为其定义属性的对象,第二个参数是一个对象,这个对象的属性就是将要定义的属性,这个对象的属性的值实际上是一个个描述符对象。
1 | var book = {}; |
读取属性的特性
前面两部分说的都是设置属性的特性,如果要读取属性的特性,则需要用到
Object.getOwnPropertyDescriptor(obj,propertyName)
方法,返回值是一个对象。如果要读取其特性的属性是一个数据属性,则这个返回的对象的属性为:configurable
、enumerable
、writable
和value
;如果要读取其特性的属性是一个访问器属性,则这个返回的对象的属性为:configurable
、enumerable
、set
和get
。
1 | var book = {}; |