Object的一些静态方法(2)
开场白
很抱歉,开始讲述知识点之前还是要让你看一段不那么重要的开场白。
今天去了自己最为向往的公司参加了第二轮面试,跟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>'
以上这点写代码的时候需要注意一下。毕竟严格模式跟非严格模式有很多行为都是有所区别的。
最后
终于到了尾声了,今天介绍的方法稍微多了一些,不过都是相关联并且比较方便记忆的,希望读者阅读的时候不会太难受。