0. 开场白
这是每天学点javascript
系列文章的第一篇,我觉得把这些相关的笔记整理成系列文章会很不错,方便读者阅读的同时也方便自己回顾。最近javascript已经成为了我生活的一部分了,虽然现在还没有工作,但是这并不妨碍我每天安排时间学习javascript。这篇文章也是学习过程中整理笔记所得。
鉴于上一篇文章《客官要来几个简单的javascript方法不》已经对Object.hasOwnProperty
, Object.propertyIsEnumerable
进行了讲述,这篇文章就不再赘述了。今天来讲点别的。
1. Object.create
可以根据原型创建对象,继承的时候很有用。
2. Object.defineProperties
与Object.defineProperty
用来设置属性的特性。
1. 正文
1. Object.create
我们都知道javascript跟python这些语言有些不同的是,他是通过继承了原型链的方式来继承父类原型的对应属性的。如果我们需要创建一个拥有字符串对象的属性的对象的时候我们实际上可以这样操作。
> var a = Object.create(String.prototype)
> a
String {}
这里可以理解为,通过字符串的原型来创建一个对应的对象,它继承了字符串原型的所有属性。不过Object.create
是ES5才有的方法,在ES3的时候并没有这种做法,那如何写出兼容代码呢?犀牛书就有这样一个例子
function inherit(p) {
if (p == null) throw TypeError();
if (Object.create) return Object.create(p);
var t = typeof p
if (t !== "object" && t !== "function") throw TypeError();
function f() {};
f.prototype = p;
return new f();
}
我们看看效果
> function inhert(p) {
... if (p == null) throw TypeError();
... if (Object.create) return Object.create(p);
... var t = typeof p;
... if (t !== "object" && t !== "function") throw TypeError();
... function f() {}; // 创建一个类
... f.prototype = p; 设置类的原型链
... return new f(); // 创建该类的对象
... }
undefined
> var b = inhert(String.prototype)
undefined
> b
String {}
跟上面运行的结果是一样的,不过这个玩意儿并不能完全取代Object.create。当传入的参数为null的时候,这个方法是会抛出异常,而Object.create
是会正常创建出一个不继承任何属性和方法的对象。看下面:
> var c = Object.create(null)
undefined
> c.toString()
TypeError: c.toString is not a function
可以看到它连最起码的toString
都没有继承。
Object.defineProperty 与 Object.defineProperties
看它们的尿性是单复数的关系,姑且认为是用来设置一个属性的特性,以及设置一个对象多个属性的特性。这些其实也是ES5才有的方法。
1. 从数据层面上来说属性有4个特征,分别是它的值(value),可写性(wriable),可枚举性(enumerable)和可配置性(configurable)。
2. 从存储器层面来说,它不具有值特性和可写性,存取器特性是读(get),写入(write),可枚举性(enumerable),可配置性(configurable)。
为了方便,我还是用犀牛书里面的类似例子来说明, 适当做了些修改。
var p = Object.defineProperties({}, {
x: {value: 1, writable: true, enumerable: true, configurable: true},
y: {value: 2, writable: true, enumerable: false, configurable: true},
z: {value: 3, writable: true, enumerable: false, configurable: false},
r: {
get: function() {return Math.sqrt(this.x * this.x + this.y * this.y)},
enumerable: true,
configurable: true
}
});
为了节省代码,我直接采用同时进行多个属性的定义,这里定义了一个对象的x, y, z属性,分别有不同的特性,r是一个存取器,这里只定义了它的get特性。
我们在node 里面尝试一下
> Object.keys(p)
[ 'x', 'r' ]
上面采用了这个方法之前有说过,就是显示出对象自有的所有可枚举的属性,因为我们的y,z的可枚举性被我设置成了false,所以这里并不会把他们列出来。而我再做如下测试
> delete p.x
true
> delete p.z
false
可以看到我这里并不能删除z属性,因为我设置了它是不可配置的。如果使用--use-strict
(严格模式)来启用node的话,
> delete p.z
TypeError: Cannot delete property 'z' of #<Object>
可见在严格模式下,删除一个特性为不可配置的属性是会报错的。而在之前非严格模式下只是会返回false而已。
另外,如果writable
设置为false的话,则这个对象对应的属性则是不可写的。在严格模式下会直接报错,而在非严格模式下则会返回你要设置的值,但是属性最后并没有任何改变。
> var a = Object.defineProperty({}, 'y', {value: '1', writable: false})
undefined
> a.y
'1'
> a.y = 12
12
> a.y // 属性最后还是没有修改
'1'
> var a = Object.defineProperty({}, 'y', {value: '1', writable: false})
undefined
> a.y = 12
TypeError: Cannot assign to read only property 'y' of object '#<Object>'
ES5中增加了这些方法来设置属性的特性,可以让我们的类,对象能够定义得更加健壮,不然的话所有属性都是可枚举,而且是可以随便修改的话,想想也是比较恐怖的。
2. 结尾
可能篇幅比较短让你失望了。因为我害怕篇幅太长会让读者难受,所以我决定每天学点javascript
系列文章把篇幅控制在每篇介绍两个左右的相关的知识点,这样或许会更方便读者阅读吧。
Happy Writing & Coding
开场白
很抱歉,开始讲述知识点之前还是要让你看一段不那么重要的开场白。
今天去了自己最为向往的公司参加了第二轮面试,跟CEO聊天的感觉真的非常好,希望自己能进去吧。但是不管怎么说,日子总得要过吧。如果真的不幸没有办法进入该公司,或许我能做的也就是整理着装,重新踏上求职之旅吧。不过在这之前,还是要每天学点javascript
吧?
今天主要讲述下面这两组方法
1. Object.keys
与Object.getOwnPropertyNames
的异同。
2. Object.seal
, Object.preventExtensions
以及Object.freeze
可以在不同程度增强对象的健壮性。
正文
1. Object.keys
跟 Object.getOwnPropertyNames
的异同
两个方法都是可以用来获取指定对象的自有属性的。那么它们有什么不同呢?
之前已经稍微提及过Object.keys
了,它是用来获取一个对象的自有属性。比如:
> var a = {x: 1, y: 2}
undefined
> a
{ x: 1, y: 2 }
> Object.keys(a)
[ 'x', 'y' ]
那它有什么局限呢?更确切地说它只能用来获取对象可枚举的自有属性,我们看看下面的例子。
> Object.getOwnPropertyDescriptor(a, 'x')
{ value: 1, writable: true, enumerable: true, configurable: true }
> Object.defineProperty(a, 'x', {enumerable: false}) // 设置x属性为不可枚举的
{ y: 2 }
> a
{ y: 2 }
> Object.keys(a)
[ 'y' ]
可见,现在我们只能获取到对象a
的自有的并且是可枚举的属性。如何获得对象a
的自有属性,并且不管它们是否是可枚举的呢?这个时候我们可以考虑用Object.getOwnPropertyNames
。
> Object.getOwnPropertyNames(a)
[ 'x', 'y' ]
这样就可以获取对象a
的所有自有的属性,包括了不可枚举的属性。如果我们需要遍历对象a
的所有自有属性,这个方法就能派上用场了。
2. Object.seal
, Object.preventExtensions
以及Object.freeze
可以在不同程度增强对象的健壮性。
这里就有一个问题,什么是对象的可扩展性?我用比较直白的话说就是“是否能够为这个对象添加属性?”。我们可以用Object.isExtensible
来判断对象是否是可以扩展的。
> Object.isExtensible(1)
false
> Object.isExtensible({})
true
> Object.isExtensible("lanzhiheng")
false
字符串不可以扩展?你在逗我吗?
对的,我们这里用的是字符串的字面量,它是不可以扩展的。做个实验:
> var str = "lanzhiheng"
undefined
> str.age = 12
12
> str.age
undefined
我们试图给str添加一个属性,然而最后查找的时候并没有设置成功,原因是
当我们添加属性的时候,JS会临时创建一个字符串的对象,并且它包装了原来的字符串字面量,所以这里并不会报错。但是当我们执行完
str.age = 12
之后这个对象自动销毁了,所以这个属性添加是没有作用的。
好啦,回到正题,下面是ES5之后才有的静态方法。我们按顺序来说说。
1. Object.preventExtensions
让对象变成不可扩展的
来看例子:
> var b = {x: 1, y: 2}
undefined
> b.z = 13
13
> Object.preventExtensions(b) // 保护可扩展性
{ x: 1, y: 2, z: 13 }
> b.k = 100
100
> b
{ x: 1, y: 2, z: 13 }
可见,一开始给对象b
添加属性是可以的,然而,我们调用了Object.preventExtensions
之后这个设置就失败了,在严格模式下甚至还会报错
TypeError: Can't add property x, object is not extensible
2. Object.seal
让对象变成不可扩展的并且把已有的属性设置成不可配置的
这里我们简单把不可配置理解成不可删除
,(当然绝不是那么简单而已)。我们来操作一下新对象c
。
> c = {x: 1, y: 2, z: 3}
{ x: 1, y: 2, z: 3 }
> c.kk = 12 // 添加属性
12
> c
{ x: 1, y: 2, z: 3, kk: 12 }
> delete c.kk // 删除属性
true
> c
{ x: 1, y: 2, z: 3 }
目前为止是很正常
的,现在用Object.seal
来处理一下好吧。
> Object.seal(c)
{ x: 1, y: 2, z: 3 }
> c.kk = 1000 // 添加属性
1000
> c // 无效
{ x: 1, y: 2, z: 3 }
> delete c.x // 删除属性
false
> c // 无效
{ x: 1, y: 2, z: 3 }
发现处理之后对c
对象进行扩展以及删除属性这些操作都不生效了(严格模式下还会报错),这就是Object.seal
方法的作用。可以保护对象的属性以及对象本身, 我们可以方便地用Object.isSealed
来判断对象是否具有这类属性。
> Object.isSealed(c)
true
同时,我们也可以用来判断对象是否具有不可扩展且属性不可配置
这些特征,即便它没有经过Object.seal
处理
> var a = {x: 1, y: 2}
undefined
// 设置对象为不可扩展的
> Object.preventExtensions(a)
{ x: 1, y: 2 }
> Object.isSealed(a)
false
// 设置对应的属性的特性为不可配置的
> Object.defineProperties(a, {x: {configurable: false}, y: {configurable: false}})
{ x: 1, y: 2 }
> Object.isSealed(a)
true
这个方法是不是还不错?另外, 虽然我们无法对对象的已有结构
进行修改,但是我们却依然可以修改对象已有的属性的值
> c
{ x: 1, y: 2, z: 3 }
> c.x = 100
100
> c
{ x: 100, y: 2, z: 3 }
要怎样更进一步方便地提高健壮性,把所有属性都弄成不可写的,并且不可配置的呢?这个时候会用到下面要介绍的Object.freeze
方法。
3. Object.freeze 让对象变成不可扩展的, 并且把已有的属性设置成不可配置并且不可写
这里不多说明,直接举例子
> var d = {x: 1, y: 2, c: 3}
undefined
> Object.freeze(d)
{ x: 1, y: 2, c: 3 }
> d.kk = 100 // 扩展对象d
100
> d
{ x: 1, y: 2, c: 3 }
> delete d.x // 删除对象d属性
false
> d
{ x: 1, y: 2, c: 3 }
> d.x = 100 // 修改对象d属性的值
100
> d
{ x: 1, y: 2, c: 3 }
可见对象d
进行了上面操作依然没有任何改变。同样的, 我们也可以用Object.isFrozen
来判断对象是否有对应的特性。
> Object.isFrozen(d)
true
这里就不用复杂的例子了,跟Object.isSealed
是差不多的。
当然,如果要对这些进行过保护的特性进行
非法
操作,在严格模式下是会抛出异常的。
> var dStrict = {x: 1, y: 2}
undefined
> Object.freeze(dStrict)
{ x: 1, y: 2 }
> dStrict.x = 100
TypeError: Cannot assign to read only property 'x' of object '#<Object>'
以上这点写代码的时候需要注意一下。毕竟严格模式跟非严格模式有很多行为都是有所区别的。
最后
终于到了尾声了,今天介绍的方法稍微多了一些,不过都是相关联并且比较方便记忆的,希望读者阅读的时候不会太难受。
Happy Writing & Coding
0. 开场白
我个人博客现在还没正式
上线,不过从简书的点击率来看,之前的文章每篇大概有100来个人会点击进来,其实这种点击我已经是比较心满意足了(如果你经历过每篇文章只有不到10个人点击的日子,或许也会理解我现在的心理状态)。
今天继续写每天学点javascript
。其实这个系列文章用这样的标题主要是为了吸引眼球,并不是说我一定每天都会发一篇这类文章。毕竟有时候事情多,如果太过强迫自己每天来连载的话在某种程度上肯定会降低了文章的质量,这是不可取的,我只能保证会坚持连载,并且尽可能多写,但不能保证每天
。当文章累积到足够的数量的时候,或许就能够让读者有一种每天学点
的感觉吧。
今天还是讲述两个方法:
Object.getPrototypeOf
获取传入参数的原型
Object.toString
, Object.prototype.toString
定义对象的字符串表示
内容比较简单,大牛们可以选择跳过,或者留言吐槽。
1. 正文
Object.getPrototypeOf
获取传入参数的原型
通过这个方法,以对象作为参数传入,则可以返回这个对象的原型,使用方式比较简单,犀牛书第三部分也有提供例子,我对例子稍做修改
> var strObject = new String("lanzhiheng") // 创建字符串对象,原型是String.prototype
undefined
> Object.getPrototypeOf(strObject) // 获取新创建对象的原型
[String: '']
我擦,这肯定看不出什么是吧,就算我这样做
> String.prototype
[String: '']
似乎也只能说明,String.prototype
碰巧换算出来是[String: '']
,谁能保证它就是创建strObject
的原型?
我们还记得用
==
比较两个指向对象的变量,只有当它们都指向同一个对象的时候才相等吗?
> {} == {} // 两个不同的对象
false
> var a = b = {}
undefined
> a == b // a和b指向同一个对象
true
用同样的原理我们可以这样判断
> Object.getPrototypeOf(strObject) == String.prototype
true
哈哈,这样就可以判断出,strObject
的原型其实就是String.prototype
。更进一步
> var obj = {};
undefined
> var nextObj = Object.create(obj) // 以obj为原型创建一个新的对象
undefined
> Object.getPrototypeOf(nextObj) == obj // 它的原型就是obj
true
这样应该了解这个方法的作用了吧?
Object.toString
, Object.prototype.toString
定义对象的字符串表示
几乎是每一个类型都有它自己的toString
类方法,当然null
这种肯定就没有了。另外,几乎所有对象也有它们对应类型的toString
方法,这个是从原型链继承来的,也就是Object.prototype.toString
。这个原型的Object.prototype.toString
方法跟Object.toString
是不一样的。前者是可以通过原型链条传承下去,而后者只是Object
这个类型的一个类方法而已。
> Object.toString == Object.prototype.toString // 它们是不一样的
false
> var b = Object.create(Object.prototype)
undefined
> b.toString == Object.prototype.toString // 通过原型链继承下去了
true
可见,对象的属性没有进行任何重载的情况下调用的依然是原型链中的Object.prototype.toString
方法。
当然,我们很少会主动去调用,一般它们会在需要的时候自动调用, 你看:
> Object.prototype.toString()
'[object Object]'
> var a = 1
undefined
> a + Object.prototype
'1[object Object]'
后面的Object.prototype
自动调用了toString
,然后才进行了字符串的拼接操作(当然变量a也进行了类似的转换)。
除此之外,Object.prototype.toString作为最为原始的
toString`版本可以用来判断对象属于什么类型。
> function classOf(p) { // 定义这个样一个方法
... if (p === null) return 'null';
... if (p === undefined) return undefined;
... return Object.prototype.toString.call(p).slice(8, -1);
... }
undefined
> classOf(1)
'Number'
> classOf(null)
'null'
为什么要调用原始版本?每个不同的类型的原型上不是都有toString吗?
正是因为每种不同类型的原型都可能有它们自己定制的toString
。我们需要的才必须是Object.prototype.toString
这个方法,如:
> (new Date()).toString()
'Wed Oct 26 2016 09:00:31 GMT+0800 (CST)'
> (new Number()).toString()
'0'
> (new Object()).toString()
'[object Object]'
只有Object.prototype.toString
会返回类似[object Number]
的结果,我们只需要拿最后的Number
便可。
但是这个东西并不能是总能判断出一个对象的所属类,如果它不是内置类型的话,我们就不能这样干了。
> function Range(x) {this.x = x} // 创建一个类
undefined
> var r = new Range() // 创建这个类的对象
> classOf(new Range())
'Object'
可见,得出的类型只是Object
。我们没办法通过这种方式得出结果Range
。这个在使用的时候还是要多加注意。
2. 最后
这两个方法还是比较容易理解的,原谅我不小心就说了那么多。很感谢你能看到最后。
Happy Writing & Coding
深入理解JavaScript中的属性和特性
JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性。
主要内容如下:
- 理解JavaScript中理解对象的本质、理解对象与类的关系、对象与引用类型的关系
- 对象属性如何进行分类
- 属性中特性的理解
第一部分:理解JavaScript中理解对象的本质、理解对象与类的关系、对象与引用类型的关系
对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。即对象是一组没有特定顺序的值,对象的每个属性或方法都有一个名字,而这个名字都映射到一个值。故对象的本质是一个散列表:其中是一组名值对,值可以是数据或函数。
对象和类的关系:在JavaScript中,对象和类没有任何关系。这是因为ECMAScript中根本就没有类的概念,它的对象与其他基于类的语言中的对象是不同的。
对象和引用类型的关系:对象和引用类型并不是等价的,因为每个对象都是基于一个引用类型创建的。
第二部分:对象属性如何进行分类
由构造函数或对象字面量方法创建的对象中具有属性和方法(只要提到属性和方法,它们一定是属于对象的;只要提到对象,它一定是具有属性和方法的(自定义除外)),其中属性又可分为数据属性和访问器属性,他们的区别如下:
- 数据属性一般用于存储数据数值,访问器属性不包含数据值
- 访问器属性多用于get/set操作
第三部分:属性中特性的理解
ECMAScript为了描述对象属性(property)的各种特征,定义了特性(attribute)这个概念。也就是说特性不同于属性,特性是为了描述属性的。下面,我将分别讲解:
- 数据属性及其特性
- 访问器属性及其特性
- 如何利用Object.defineProperties()方法定义多个特性
- 如何利用Object.getOwnPropertyDescripter()方法读取属性的描述符以读取属性的特性
1.数据属性及其特性
刚刚我们说过,数据属性是用于存储数据数值的,因此数据属性具有一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性,由于ECMAScript规定:在JavaScript中不能直接访问属性的特性(注意:不是不能访问),所以我们把它放在两组方括号中。如下:
- [[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由数据属性修改为访问器属性
- [[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
- [[Writable]]:默认值为true,表示能否修改属性的值,这是与[[Configurable]]不同之处。
- [[Value]]:默认值为undefined,这个值即为属性的属性值,我们可以在这个位置上读取属性值,也可以在这个位置上写入属性值。
- 注意:上述的默认是指通过构造函数或对象字面量创建的对象所自身拥有的属性,而不是下面要介绍的Object.defineProperty()方法
这些特性都具有默认值,但是如果这些默认值不是我们想要的,该怎么办呢?当然就是修改啦!我们可以通过Object.defineProperty()方法来修改属性默认的特性。英文difineProperty即为定义属性的意思。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中第三个参数描述符对象是对象字面量的方法创建的,里面的属性和属性值实际上保存的是要修改的特性和特性值。
下面通过几个例子来深入理解。
a
1
2
3
4
5
6
7
8
9
|
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
1
2
3
4
5
6
7
|
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
1
2
3
4
5
6
7
8
|
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
1
2
3
4
5
6
7
8
|
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
1
2
3
4
5
6
7
8
9
10
11
|
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
1
2
3
4
5
6
7
8
9
10
11
|
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()来定义。(通过这个规定我们就能准确地判断出访问器属性和数据属性了)
通过下面这个例子来深入理解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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 |
几个需要深入理解的地方:
- 访问器属性不能直接定义,必须使用Object.defineProperty()来定义,且该属性具有set和ger特性,于是可以判断,_year和edition是数据属性,而year是访问器属性。
- 我们看到_year这个数据属性前面是以_(下划线)开头的,这个一种常用的记号,用于表示只能通过对象方法访问的属性。从上面的例子中可以看到get相当于描述符对象的一个方法,而_year正是在这个对象方法访问的属性。而edition既可以通过对象方法访问,也可以由对象直接访问。
1
- book.year表示正在读取访问器属性,这时会调用get函数,并返回了2004这个有效的值。
- book.year=2005表示写入访问器属性,这时会调用set函数并传入新值,即将2005传给newValue,这个函数决定如何处理数据。
- 这时使用访问器属性的常见方法-即设置一个属性的值会导致其他属性发生变化。
3.如何利用Object.defineProperties()方法定义多个特性
显然,一个对象不可能只具有一个属性,因此,定义多个属性的可能性很大,于是JavaScript提供了Object.defineProperties()方法解决这个问题。这个方法接收两个参数,第一个是要定义属性所在的对象,第二个是一个对象字面量方法创建的对象,对象的属性名即为要定义的特姓名,对象的属性值又是一个对象,这个对象里的属性名和属性值分别是特性名和特性值(这里不是很好理解,看例子即可)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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; } } } }); |
4.如何利用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虽然是属性的一个特性,但是它也是函数。 |
点击这里返回页首