JavaScript中属性和特性是彻底不一样的两个概念,这里我将根据本身所学,来深刻理解JavaScript中的属性和特性。函数
主要内容以下:this
对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性能够包含基本值、对象或者函数。即对象是一组没有特定顺序的值,对象的每一个属性或方法都有一个名字,而这个名字都映射到一个值。故对象的本质是一个散列表:其中是一组名值对,值能够是数据或函数。code
对象和类的关系:在JavaScript中,对象和类没有任何关系。这是由于ECMAScript中根本就没有类的概念,它的对象与其余基于类的语言中的对象是不一样的。对象
对象和引用类型的关系:对象和引用类型并非等价的,由于每一个对象都是基于一个引用类型建立的。ip
由构造函数或对象字面量方法建立的对象中具备属性和方法(只要提到属性和方法,它们必定是属于对象的;只要提到对象,它必定是具备属性和方法的(自定义除外)),其中属性又可分为数据属性和访问器属性,他们的区别以下:get
get/set
操做ECMAScript为了描述对象属性(property)的各类特征,定义了特性(attribute)这个概念。也就是说特性不一样于属性,特性是为了描述属性的。下面,我将分别讲解:it
Object.defineProperties()
方法定义多个特性Object.getOwnPropertyDescripter()
方法读取属性的描述符以读取属性的特性1.数据属性及其特性io
刚刚咱们说过,数据属性是用于存储数据数值的,所以数据属性具备一个数据值的位置,在这个位置能够读取和写入值。数据属性有4个描述其行为的特性,因为ECMAScript规定:在JavaScript中不能直接访问属性的特性(注意:不是不能访问),因此咱们把它放在两组方括号中。以下:console
[[Configurable]]
:默认值为true
,a、表示可否经过delete
删除属性从而从新定义属性 b、可否修改属性的特性[[Enumerable]]
:默认值为true
,表示可否经过for-in
循环返回该属性(因此:若是为false
,那么for-in
循环无法枚举它所在的属性)[[Writable]]
:默认值为true
,表示可否修改属性的值,这是与[[Configurable
]]不一样之处。[[Value]]
:默认值为undefined
,这个值即为属性的属性值,咱们能够在这个位置上读取属性值,也能够在这个位置上写入属性值。Object.defineProperty()
方法这些特性都具备默认值,可是若是这些默认值不是咱们想要的,该怎么办呢?固然就是修改啦!咱们能够经过Object.defineProperty()
方法来修改属性默认的特性。英文difineProperty即为定义属性的意思。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中第三个参数描述符对象是对象字面量的方法建立的,里面的属性和属性值实际上保存的是要修改的特性和特性值。table
下面经过几个例子来深刻理解。
a
var person={}; Object.defineProperty(person,"name",{ writable:false, value:"zhuzhenwei" }); console.log(person.name);//zhuzhenwei person.name="heting"; console.log(person.name);//zhuzhenwei
这里我用对象字面量的方法建立了一个对象,可是没有同时建立方法和属性。而是利用了Object.defineProperty()
方法来建立了属性和修改了默认值。这里将writable
设置为false
,因而后面我试图修改person.name
时,是无效的。
b
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei" }); console.log(person.name);//zhuzhenwei person.name="heting"; console.log(person.name);//zhuzhenwei
注意看这个例子,这个例子中我删去了writable:false
,为何仍是不能修改呢?这是由于以前我在介绍特性时,前三个默认为ture
,是在建立对象并建立属性的状况下获得的。对于经过调用Object.defineProperty()
方法建立的属性,其前三个特性的默认值均为false
,这里须要注意。
c
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:false }); console.log(person.name);//zhuzhenwei delete person.name; console.log(person.name);//zhuzhenwei
这里咱们将新建的属性name
的特性设置为了configurable:false
;所以下面删除属性的操做是无效的。根据b,可知configurable
,默认就是false
,即便去掉也不可修改。
d
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//zhuzhenwei delete person.name; console.log(person.name);//undefined
在这里我将默认的configurable
的值由默认的false
修改成了true
,因而变成了可配置的,那么最后就成功删除了。
e
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:false }); console.log(person.name);//zhuzhenwei Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
若是以前已经设置成为了false
,那么后面再改为true
也是徒劳的,即:一旦把属性设置成为不可配置的,就不能再把它变回可配置了。
f
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…) var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", }); console.log(person.name);//zhuzhenwei Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
这里能够说明,即便前一步咱们无论默认的configurable:false
,后面获得的还是不可配置。因而,能够得出结论,为了可配置,必须在第一次调用Object.defineProperty()
函数时就将默认的值修改成true
。
2.访问器属性及其特性
以前提到,访问器属性不包含数据值,他们包含一对getter
函数和setter
函数(这两个函数不是必须的)。在读取访问器属性时,会调用getter
函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter
函数并传入新值,这个函数负责决定如何处理数据。一样,因为不能经过JavaScript来直接访问获得访问器属性的特性,因此下面列出的特性将由[[]]括起来以做区分。
[[Configurable]]
:默认值为true
,a、表示可否经过delete
删除属性从而从新定义属性 b、可否修改属性的特性c、可以把属性由访问器属性修改成数据属性[[Enumerable]]
:默认值为true,表示可否经过for-in
循环返回该属性(因此:若是为false
,那么for-in
循环无法枚举它所在的属性)[[Get]]
:在读取属性时调用的函数。默认值为undefined
关键:特性能够是一个函数[[Set]]
: 在写入属性时调用的函数。默认值为undefined
关键:特性能够是一个函数get
和set
函数也属于属性的特性,那么他们就有可能(说有多是由于这两个函数也不是必须的)出如今Object.defineproperty
的第三个参数描述符对象的属性中。注意:1.相对于数据属性,咱们发现访问器属性中没有writable
特性和value
特性。这是由于访问器属性不包含数据值,那么咱们怎么固然就不可修改属性的值(用不到writable
特性),更不用考虑value
了。
2.访问器属性不能直接定义,必须是用Object.defineProperty()
来定义。(经过这个规定咱们就能准确地判断出访问器属性和数据属性了)
经过下面这个例子来深刻理解:
var book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){<br> return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); book.year=2005; console.log(book.edition);//2
几个须要深刻理解的地方:
1.访问器属性不能直接定义,必须使用Object.defineProperty()
来定义,且该属性具备set
和get
特性,因而能够判断,_year
和edition
是数据属性,而year
是访问器属性。
2.咱们看到_year
这个数据属性前面是以_(下划线)开头的,这个一种经常使用的记号,用于表示只能经过对象方法访问的属性。从上面的例子中能够看到get至关于描述符对象的一个方法,而_year
正是在这个对象方法访问的属性。而edition
既能够经过对象方法访问,也能够由对象直接访问。
book.year
表示正在读取访问器属性,这时会调用get
函数,并返回了2004这个有效的值。book.year=2005
表示写入访问器属性,这时会调用set
函数并传入新值,即将2005传给newValue
,这个函数决定如何处理数据。3.如何利用Object.defineProperties()
方法定义多个特性
显然,一个对象不可能只具备一个属性,所以,定义多个属性的可能性很大,因而JavaScript提供了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(){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } });
Object.getOwnPropertyDescripter()
方法读取属性的描述符以读取属性的特性 咱们可使用Object.getOwnPropertyDescripter()
方法来取得给定属性的描述符。getOwnPropertyDescripter
即为取得自身属性描述符的意思。这个方法接收两个参数:属性所在的对象要要读取其描述符的属性名称。返回一个对象。
对于访问器属性而言,这个对象的属性有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(){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } }); var descriptor=Object.getOwnPropertyDescriptor(book,"_year"); console.log(descriptor.value);//2004 console.log(descriptor.configurable);//false 由于经过Object.defineProperties()方法建立的属性的特性configurable enumerable都是false console.log(typeof descriptor.get);//undefined 注意:这是数据属性,是不具备get特性的 var descriptor=Object.getOwnPropertyDescriptor(book,"year"); console.log(descriptor.value);//undefined console.log(descriptor.enumerable);//false console.log(typeof descriptor.get);//function get虽然是属性的一个特性,可是它也是函数。