概述
除了字符串、数字、undefined、null、布尔值外,JavaScript中的值全是对象。
每个属性都有属性特性:
可写 ,表明是否可以设置该属性的值可枚举,表明是否可以通过for/in循环返回该属性可配置,表明是否可以删除或修改该属性
每个对象除了属性之外,还有三个相关的对象特性:
对象的原型(prototype)指向另一个对象,本对象的属性继承自它的原型对象对象的类(class)是一个标识对象类型的字符串对象的扩展标记(extensible flag)指明了是否可以向该对象添加新属性
创建对象
当属性名中有空格,连字符等需要用字符串表示
对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候,也都会计算它的每个属性的值。也就是说,如果在一个重复调用的函数循环体内使用了对象直接量,它将创建很多新对象,并且每次创建的对象的属性值也有可能不同。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。
没有原型的对象位数不多,Object.prototype就是其中之一。它不继承任何属性,其它原型对象都是普通对象,普通对象都具有原型。所有的内置构造函数(以及大部分自定义的构造函数)都具有一个继承自Object.prototype的原型
ES5定义了一个名为Object.create()的方法,它创建一个新对象,其中第一个参数是这个对象的原型,Object.create()提供第二个可选参数,用以对对象的属性进行进一步的描述。
Object.create()是一个静态函数,而不是提供给某个对象调用的方法.
var 0 =Object.create({x:1 ,y:2});
可以通过传入参数null来创建一个没有原型的新对象,但通过这种方式创建的对象不会继承任何东西,甚至不包含基础方法,比如toString()
如果向创建一个普通的空对象(比如{}或new Object()创建的对象),需要传入Object.prototype:
var o3 = Object.create(Object.prototype)
//等同于new Object() 和{}
如果给继承对象的属性赋值,则这些属性只会影响这个继承对象自身,而不是原始对象。
属性的查询和设置
对于点(.)来说,右侧必须是一个以属性名称命名的简单标识符。对于方括号来说([]),方括号内必须是一个计算结果为字符串的表达式,这个字符串就是属性的名字。
ES5允许在点运算符后面直接使用保留字。
当使用方括号时,我们说方括号内的表达式必须返回字符串。其实更严格地讲,表达式必须返回字符串或返回一个可以转换为字符串的值。
JavaScript对象都是关联数组。
当通过点运算符(.)访问对象的属性时,属性名用一个标识符来表示。标识符必须直接出现在JavaScript程序中,它们表示数据类型,因此程序无法动态修改。而[]则可以
var addr = ""
for(i =0;i>4;i++){
addr+=customer["address"+i]+'\n'
}
现在假设给对象o的属性x赋值,如果o中已有属性x(非继承),那么这个赋值操作只改变这个已有属性x的值。如果o中不存在属性x,那么赋值操作给o添加一个新属性x。如果之前o继承自属性x,那么这个继承的属性就被新创建的同名属性覆盖。
在JavaScript中,只有在查询属性时才会体会到继承的存在,而设置属性则和继承无关,这是JavaScript的一个重要特性,该特性可以让程序员有选择地覆盖继承的属性。
属性赋值操作首先检查原型链,以此判定是否允许赋值操作。如果继承自一个只读属性x,那么赋值操作是不允许的。
属性赋值要么失败,要么创建一个属性,要么在原始对象中设置属性。(?P126)
查询一个不存在但属性并不会保存。
如果对象不存在,那么试图查询这个不存在的对象的属性就会报错。null和undefined值都没有属性,因此查询这些值的属性会报错。
var len = book && book.subtitle && book.subtitle.length
在严格模式下,任何失败的属性这只操作都会抛出一个类型错误异常。
在以下场景给对象o设置属性p会失败
o中的属性p是只读的:不能给只读属性重新赋值(defineProperty()方法中有例外,可以对可配置的只读属性重新赋值)o中的属性p是继承属性,且他是只读的:不能通过同名自有属性覆盖只读的继承属性o中不存在自有属性p:o没有使用setter方法继承属性p,并且o的可扩展性是false。如果o中不存在p,且没有setter方法可供条约,则p一定会添加至o中。但如果o不是可扩展的,那么在o中就不能定义新属性
删除属性
delete指数断开属性和宿主对象但联系,而不会去操作属性中的属性。因此只在销毁对象的时候,要遍历属性中的属性,依次删除
delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除,而且这会影响到所有继承自这个原型的对象)
delete不能删除那些可配置性为false的属性(尽管可以删除不可扩展对象的可配置属性)。严格模式下,删除一个不可配置属性会报一个类型错误。
在严格模式下,delete后跟随一个非法的操作数(比如x),则会报一个语法错误,因此必须显式指定对象及其属性
检测属性
in用于检查对象是否具有某个属性(自有和继承),in可以分辨属性不存在和属性为undefined
对象的hasOwnProperty()方法用来检测给的的名字是否是对象的自有属性,对于继承属性会返回false
propertyIsEnumerable()是hasOwnPeoperty()的增强版,只有检测到该自有属性的可枚举性为true时他才返回true
枚举属性
Object.getOwnPropertyNames()和Object.keys()类似,指数它返回对象的所有自有属性的名称,而不仅仅是可枚举的属性。
属性getter和setter
当程序查询存取器属性的值时,JavaScript调用getter方法(无参数)。这个方法的返回值就是属性存在表达式的值。当程序这种一哦过存取器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当作参数传入setter。从这种意义上讲,这个方法赋值设置属性值。可以忽略setter方法的返回值。
与数据属性不同,存取器属性不具有可写性。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果只有一个getter方法,那么是只读属性。如果只有setter方法,那即是只写属性。读取只写属性总是返回undefined
和数据属性已有,存取器属性是可以继承的
属性的特性
存取器属性不具有值(value)特性和可写性,它们的可写性是由setter方法存在与否决定的。因此存取器属性的4个特性是独缺、写入、可枚举性和可配置性。
为了实现属性的描述对象的属性有value、writable、enumerable和configurable。存取器属性的描述性对象则用
get属性和set属性代替value和writable。其中writable、enumerable和configurable都是布尔值。get和set属性是函数值
Object
var o={x:1}
Object.getOwnPropertyDescriptor(o,"x")
//Object {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor()只能获得自有属性的描述符
要想设置属性的特性,或者想让新建属性具有某种特性,需要调用Object.defineProperty(),传入修改的对象,要创建或修改的属性名称以及属性描述符对象
Object.defineProperty(o,"x",{value:2,enumerable:false}) //Object {x: 2}
o.x //2
Object.keys(o) //[]
传入Object.defineProperty()的属性描述符对象不必包含所有4个特性。对于新创建的属性来说,默认的特性值是false或undefined。对于修改已有的属性,则默认不做任何改变。注意,该方法要么是修改已有属性有么新建自有属性,不能修改继承属性
如果要同时修改或创建多个属性,则需要使用Object.defineProperties()。第一个参数是要修改的对象,第二个参数则是一张映射表,要包含新建或修改的属性的名称,以及它们的属性描述符
var p = Object.defineProperties({},{
x:{value:1,writable:true,enumerable:true,configurable:true},
y:{value:1,wtitable:false},
r:{
get: function(){return Math.sqrt(this.x*this.x+this.y*this.y)}
}
})
Object.defineProperties()会返回修改后的对象(Object.defineProperty()一样)
对于那些不允许创建或修改的属性来说,如果用defineProperty对其操作(新建或修改),会抛出类型错误异常
P137
对象的三个属性
原型属性是在实例对象创建之初就设置好的,通过对象直接量创建的对象使用Obejct.prototype作为它们的原型。通过new创建的对象使用构造函数的prototype属性作为她的原型。通过Obejct.create()创建的对象的使用第一个参数(也可以是null)作为它们的原型
在ES5中,将对象作为参数传入Obejct.getPrototypeOf()可以查询它的原型。ES3中可以用o.constructor.prototype来检测对象原型,但这种方法并不可靠——对于对象直接量有效,但对于通过Object.create()创建的对象则往往不是这样
检测一个对象是否是另一个对象的原型(或处于原型链),可以使用isPrototypeOf()方法.比如p.isPrototypeOf(o)可以检测p是不是o的原型
要想获得对象的类,可以调用对象的toString()方法,然后提取已返回字符串的第8个到倒数第二个位置直接到字符(由于很多对象重写toString,因此应该采用call)
Object.prototype.toString(0).slice(8,-1)
通过内置构造函数创建的对象包含类属性,它与构造函数名称相匹配。宿主对象也包括有意义的类属性,但着依赖于具体的js实现。自定义构造函数创建的对象的类属性是Object,而对象直接量和Object.create()也是如此
对象的可扩展性用于表示是否可以给对象添加新对象。在ES5中,所有的内置对象和自定义对象都是可扩展的,除非手动转化
Object.preventExtensions()可以将对象转化为不可扩展,而不可扩展无法变为可扩展。而Object.isExtensible()则用来判断。此外,如果给一个不可扩展的对象的原型添加属性,这个不可扩展的对象统一会继承这些新属性
Object.seal()个Object.preventExtensions()类似,除了能够将对象设置为不可扩展的,还可以将对象的所欲呕自有属性都设置为不可配置的。
对于那些已封闭起来的对象是不可解封的。可以用Object.isSealed()来检测对象是否封闭
Object.freeze()则更严格,除了将对象设置为不可扩展和将属性配置为不可配置,还会将所有属性设置为只读。setter不受影响
Object.isFrozen()用于检测
序列化对象
ES5内置了JSOO.stringify()和JSON.parse()用来序列号和还原js对象。
JSON支持对象、数组、字符串、无穷大数字、布尔值和null的序列号和还原。(日期会变为字符串)
对象方法
Object中默认的toLocaleString()方法并不作任何本地化操作,只是调用toString()方法并返回对应值。Date()和Number()的toLocaleString()方法做了定制,而Array会对每一个元素都调用toLocaleString()
对于需要执行序列化的对象来说,JSON.stringify()方法会调用toJSON()方法。若待序列化的对象存在该方法,则调用,返回值即是序列化的结果,而不是原始对象