对象是属性的无序集合。因此,这一章主要从对象和属性两个方面来阐述。
1. 对象
1.1 对象的定义
对象是属性的无序集合,也可以看做是从字符串到值的映射。
1.2 对象的分类
对象可分为三类:
- 内置对象(native object):由ECMAScript规范定义的对象。如数组、函数、日期、正则表达式等都是内置对象。
- 宿主对象(host object):JS解释器所嵌入的宿主环境(比如Web浏览器)定义的。如客户端JS中的HTMLElement对象就是宿主对象。
- 自定义对象(user-defined object):运行中的JS代码创建的对象。
1.3 对象的操作
1.3.1 创建对象
创建对象主要有3种方法:对象直接量、new运算符、Object.create()
(1) 对象直接量:
`var empty = {}`
`var point = {x:0,y:0}`
(2) new运算符:new + 函数调用(该函数称为构造函数constructor).使用对象直接量创建对象相当于使用 new Object()
(3) `Object.create()`
**每一个JS对象(null除外)都和另一个对象相关联,另一个对象就是该对象的原型。** 使用对象直接量创建的对象的原型就是Object.prototype,使用new运算符创建的对象的原型是构造函数的prototype的值。
Object.create()接受两个参数。第一个是要创建的对象的原型对象,第二个可选,内容是属性描述符。
对于前两种创建对象的方法,用Object.create()表示即为:Object.create(Object.prototype),Object.create(Func.prototype).
当有第二个参数时:
var bob = Object.create(userB, {
'id' : {
value: MY_GLOBAL.nextId(),
enumerable:true // writable:false, configurable(deletable):false by default
},
'name': {
value: 'Bob',
enumerable: true
}
});
1.3.2 序列化对象
对象序列化是指将对象的状态转化为字符串,也可将字符串还原为对象。函数、RegExp、Error、undefined值不能序列化和还原。
`JSON.stringify()`将任意的JavaScript值序列化成JSON字符串,并且**只能序列化对象的可枚举属性**。
`JSON.parse()`将一个 JSON 字符串解析成为一个 JavaScript 值。
1.4 对象的方法
所有的对象都会从Object.prototype继承属性和方法(除了以null为原型),这里先介绍Object.prototype的几个方法,其他的将会在后面的部分介绍。
- `toString()`:返回调用这个方法的对象值的字符串。返回信息少。在需要将对象转换为字符串的时候,JS会调用这个方法。
- `toLocaleString()`:返回对象的本地化字符串。Object的该方法调用toString(),Date 和 Number 则对日期、数字做本地化的转换。
- `valueOf()`:返回对象的原始值。
1.5 对象的属性
- 原型属性:每个对象都有一个原型(Object.prototype除外),ECMAScript提供了两种方法查询对象的原型。
a. `Object.getPrototypeOf()`:返回作为参数传入的对象的原型。
b. `isPrototypeOf()`:检测一个对象是否是另一个对象的原型。p.isPrototypeOf(o)检测p是否是o的原型。
- 可扩展性:是否可以给对象添加新属性。主要**目的是将对象锁定,避免外界干扰**。关于可扩展性有以下几种方法:
a. `Object.isExtensible()` :检测传入的对象是否是可扩展的,返回布尔值。
b. `Object.preventExtens()` :将对象设置为不可扩展的,意味着不能给对象添加新的属性。**将对象设置为不可扩展后不能重新设置为可扩展。**
c. `Object.seal()`:将对象设置为不可扩展+不可配置自有属性,意味着不能给对象添加新的属性+不能对已有属性进行删除或配置。
d. `Object.isSeal()`:判断对象是否封闭(seal)。
e. `Object.freeze()`:不可扩展+不可配置+数据属性只读。
f. `Object.isFrozen()`判断对象是否冻结(freeze)
2.属性
2.1 属性的分类
a.按照原型和继承可分为: 继承属性和自由属性。
b.按照属性的描述符可分为: 数据属性和存取器属性(accessor property)。
2.2 属性的描述符
属性的描述符主要是对针对属性可以进行的操作的描述。
数据属性和存取器属性共有的属性描述符是:
- enumerable:是否可枚举,即能佛欧通过for/in 或者 Object.keys()循环。
- configurable: 是否可以删除属性或者对属性描述符进行修改。如果configurable为false,则不能对writable之外的描述符进行修改,同时数据属性和存取器属性不可转换。也不能将可写性从false改为true。
数据属性单独有的属性描述符是:
- value:属性的值。
- writable:属性的值是否可通过赋值运算符修改。
存取器属性单独有的属性描述符是:
- set:给属性提供setter方法,若无则为undefined.
- get:给属性提供getter方法,若无则为undefined.
针对属性的描述符有以下几种方法:
- `Object.getOwnPropertyDescriptor()` :获得对象的特定的**自有属性**的属性描述符。第一个参数为对象,第二个为属性名。
- `Object.defineProperty(obj, prop, descriptor)` :给对象添加新的属性或者修改已有属性的属性描述符,并返回该对象。通过该方法添加的属性,其默认的属性描述符都为false.通过赋值运算符设置的属性的属性描述符默认为true.
- `Object.defineProperties(obj, props)`:同时修改或添加多个属性。
2.3 对属性的操作
2.3.1 查询和设置
有两种方式可查询设置对象的属性:.和[]。.的右侧必须是有效标识符且不能是保留字。而[]则可以动态获取属性以及可以转换为字符串的属性。由于[]访问与数组很相似,因此也叫作关联数组。
属性的赋值会先检查原型链,若原型链有该属性且该属性为不可写,则不能给该属性赋值。若原型链存在且可写,或者原型链不存在该属性,则该属性就变为自有属性,且不会修改原型对象的属性值。
2.3.2 删除属性
`delete`可删除对象的自有属性且configurable为true的属性。注意:delete只是断开属性与对象的关系,而不是操作属性中的属性。由此可能造成内存泄漏。
2.3.3 检测属性
检测属性主要检测两方面:(1)属性是否存在在对象中;(2)哪些属性是自有属性.提供的方法如下:
- `in`操作符。检测自有和继承属性中是否包含该属性。同时若属性值为undefined,返回true,若属性不存在返回false. property in obj
- `hasOwnProperty()`: obj.hasOwnProperty("x")检测属性”x”是否是obj对象的自有属性。
- `propertyIsEnumerable()`: obj.propertyIsEnumerable("x")检测属性”x”是否是obj对象的自有属性+该属性是否可枚举.当同时为true时才返回true.
2.3.4 枚举属性
枚举属性主要有三种方法:
- `for/in`:遍历所有**可枚举**的自有+继承属性。把属性名称赋值给循环变量。(枚举+所有)
- `Object.keys()`:以**数组形式**返回对象的所有**可枚举**的**自有属性**的名称。(枚举+自有)
- `Object.getOwnpropertyNames()`:以**数组形式**返回对象的所有自有属性。(自有+可枚举+不可枚举)