这次我们好好聊一聊对象这个东西,本次说的不是array
,也不是function
,而是object
基础概念
对象是一种特殊的数据类型,这种数据类型还有其他的很多叫法,比如“散列”,“散列表”,“字典”,“关联数组”。
对象按照创建者的角度,可以分为三类:内置对象
:是由javascript语言本身自定义的对象,大多数是一些预定好的构造函数类,比如Array
,Date
,Function
宿主对象
:是指javascript解释器所嵌入的宿主环境定义的对象,比如HTMLElement就是宿主对象。自定义对象
:是指由开发者在代码中所创建的对象。
我们知道对象的表达方式是:'属性':'值'
属性按照来源不同,可以分为两类:自有属性
:是指直接在对象中定义的属性。继承属性
:是指在对象原型链中的属性。
与此同时,对象的属性还具有一些特性:可写
:代表是否可以设置该对象所对应的该属性的值。可枚举
:代表是否可以通过api枚举出该属性。可配置
:代表是否可以删除或修改该属性。
记住上述概念,有助于我们进行下一步的理解。
创建对象
创建对象有三种方式:
第一种:对象直接量
var o = {}
var obj = {
a: 1,
'b': 2,
'min title': 3
}
第二种:通过new创建对象
var o = new Object()
第三种:Object.create()
该方法支持传入两个参数,第一个参数是对象的原型(就是所创建出的对象的_proto_
的指向),第二个参数是对属性进一步的描述(该参数可选,参数内容会在后面详解)
以下三种方式是等价的:
检测对象原型的方法除了instanceof
之外,还有一个isPrototypeOf()
,我们来看一下使用:
对象的getter和setter
我们首先再明确一个概念:
我们常见的{a: 1}
中的a
叫做数据属性
,除此之外还有一个叫做存取器属性
,存取器属性的值的读取和设置,都是由getter和setter控制的。
var o = {
p: 0,
set q(value) {
this.p = value
},
get q() {
return this.p+=100
}
}
o.q = 1
console.log(o.q) // => 101
console.log(o.q) // => 201
其中,对象中的函数定义没有使用function
关键字,使用的而是get
和set关键字
处理可以这样定义存取器属性,我们还可以利用其他的方法定义存取器属性
,就是我们熟知的Object.defineProperty()
和Object.definePeoperties()
首先,我们介绍一下Object.defineProperty()
定义数据属性
的方式
var o = {}
// 定义属性
Object.defineProperty(
o,
'x',
{
value: 1,
writable: true,
enumerable: true,
configurable: true
}
)
// 修改属性
Object.defineProperty(
o,
'x',
{
value: 2,
writable: false
}
)
这个函数共有三个参数:
第一个参数是,需要加属性的对象。
第二个参数是,添加的属性的名称。
第三个参数是定义的配置项:
第一个配置就是这个属性所对应的值。
剩余三个配置就是对应到文章一开始所提到属性三个特性可写
,可枚举
,可配置
。
这四个配置项都不是必须填写。假如对于新创建的属性,value不填,默认值就是undefined
,其他配置项的缺省值是false
。假如对于修改已有的属性来说,不填写的配置项就不做修改。
我们再看一下Object.defineProperty()
定义存取器属性
的方式
其中,需要注意的是,在定义存取器属性
时无法定义,value
和writable
配置项,因为定义的get
和set
从某种意义上代替了前两个配置项。
var o = {y: 1}
Object.defineProperty(
o,
'x',
{
set(value) {
this.y = value
},
get() {
return this.y+=100
},
enumerable: true,
configurable: true
}
)
Object.defineProperty
只能单个定义或修改对象属性,Object.defineProperties
提供了批量解决的办法,如下:
var o = {}
Object.defineProperties(
o,
{
x: {
set(value) {
this.y = value
},
get() {
return this.y+=100
},
enumerable: true,
configurable: true
},
y: {
value: 1,
writable: false,
enumerable: true,
configurable: true
}
}
)
Object.defineProperty
和Object.defineProperties
对于属性的修改是有规则和要求的,如下:
- 如果对象是不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。
- 如果属性是不可配置的,则不能修改它的可配置性和可枚举性。
- 如果存取器属性是不可配置的,则不能修改其getter和setter方法,也不能将它转换为数据属性。
- 如果数据属性是不可配置的,则不能将它转换为存取器属性。
- 如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false。
- 如果数据属性是不可配置且不可写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的(实际上是先将它标记为可写的,然后修改它的值,最后转换为不可写的)。
在这里,我们再看一下上面的Object.create
方法,Object.create
的第二个参数是和Object.defineProperties
第二个参数一样的,缺省值同样为undefined
和false
没有提供选项去配置属性特性的方法,这些属性默认都是true,例如:new
关键字和对象直接量的方式创建对象,以及最上面对象直接量的方式设置getter和setter
对象的扩展性
对象的扩展性是什么,其实就是指对象能否添加新的属性,默认情况下所有的内置对象和自定义对象都是可扩展的。除非我们人为的改变它:
Object.preventExtesions()
可以传入一个参数,就是你要取消扩展功能的对象,操作后,这个对象会不能扩展(即不能添加新属性),且不能恢复,操作只影响该对象本身,不对其他原型链操作产生影响。
我们可以利用Object.esExtensible()
判断这个对象是否可扩展,传入一个对象,返回布尔值。
Object.seal()
可以将对象封闭,效果和上面的Object.preventExtesions()
一样,增加的效果是将这个对象的自有属性设置为不可配置(即将configurable设为false),同样不能解封。
我们可以利用Object.isSealed()
来判断这个对象是否封闭,传入一个对象,返回布尔值。
Object.freeze()
可以将对象冰冻,效果和上面的Object.seal()
一样,增加的效果是将这个对象的自有属性设置为不可写(即将writable设为false),同样不能解冻。Object.isFrozen()
可以判断这个对象是否冰冻。
根据属性查询值
根据属性查询值的方式我们当然是众所周知了,[]
和.
,当然他们也可以设置和修改可写性为true的自有属性值
var o = {}
Object.defineProperties(
o,
{
x: {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
y: {
value: 1,
writable: false,
enumerable: true,
configurable: true
}
}
)
o.x // => 1
o['x'] = 5
o.x // => 5
o.y = 5
o.y // => 1
删除属性
delete
可以用户删除对象属性,能删除的属性只是该对象的自有属性
且属性配置性为true
的属性
让人感到意外的是,delete
只是断开属性和宿主对象的联系,而不会去操作属性中的属性。
检测属性是否存在
检测属性共有三种方式,in
操作符,hasOwnProperty
和propertyIsEnumerable
in
可以检测对象的自有属性
和继承属性
,不受属性特性的影响
var o = {x: 1}
'x' in o // true
'y' in o // false
'toString' in o // true
hasOwnProperty
只能检测对象的自有属性
,不能检测继承属性
,不受属性特性的影响
var o = {x: 1}
o.hasOwnProperty('x') // true
o.hasOwnProperty('y') // false
o.hasOwnProperty('toString') // false
propertyIsEnumerable
只能检测自有属性
,且要求这个属性的可枚举性为true
枚举全部属性
首先第一个方法是for/in
,这个方法可以枚举出当前对象可枚举(属性枚举性为true)的自有属性和继承属性
var o = Object.create({z: 1})
Object.defineProperties(
o,
{
x: {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
y: {
value: 1,
writable: false,
enumerable: false,
configurable: true
}
}
)
for (p in o) {console.log(p)} // => x, z
第二个方法是Object.keys()
,这个方法可以枚举出当前对象的可枚举(属性枚举性为true)的自有属性
,返回值是一个数组
var o = Object.create({z: 1})
Object.defineProperties(
o,
{
x: {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
y: {
value: 1,
writable: false,
enumerable: false,
configurable: true
}
}
)
Object.keys(o) // => ['x']
第三个方法是Object.getOwnPropertyNames
,它可以枚举出当前对象所有的自有属性(不受枚举性影响)
Object.defineProperties(
o,
{
x: {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
y: {
value: 1,
writable: false,
enumerable: false,
configurable: true
}
}
)
Object.getOwnPropertyNames(o) // => ['x', 'y']
对象的序列化和反序列化
JSON.stringify()
和JSON.parse()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。