6.JavaScript对象
对象
JavaScript中的对象是由键值对组成,每个键值对可以称为属性,静态数据属性包括名字和四个特性,存取器属性则略有不同,对象本身也有三个属性,分别是原型属性,类属性以及可拓展属性,Object.prototype中存有一些可供所有对象使用的方法,如toString(),valueOf()等
对象是JavaScript的基本数据类型,对象可以看成是属性的无序集合,每个属性都是一个键值对,属性名通常是字符串,因此我们可以对象看成是从字符串到值的映射.然而对象不仅仅是字符串到值的映射,还可以从一个称为原型的对象继承属性.
对象的属性包括名字和值,属性名可以是包含空字符串在内的任意字符串,但属性名不能同名,值可以是JavaScript任意值,或者是getter和setter函数.
每个属性还有一些与之相关的值,称为属性特性(这些特性是可以加以配置的):
- 可写,表明是否可以设置该属性的值
- 可枚举,表明是否可以通过for/in循环返回该属性
- 可配置,表明是否可以删除或修改属性
除了包含属性之外,每个对象还有相关的对象特性:
- 对象的原型指向另一个对象,本对象的属性继承自它的原型对象
- 对象的类是一个标识对象类型的字符串
- 对象的拓展标记指明了是否可以向该对象添加新属性
创建对象
1.直接使用对象直接量
对象直接量是由若干键值对组成的映射表,键值对中间使用冒号,每对键值对用逗号分隔,整个映射表用花括号括起来.
对象直接量是一个表达式,表达式每次运算都创建并初始化一个新的对象
var empty = {};
var point = {
x:0,
y:1
};
2.通过new创建对象
new运算符创建并初始化一个新对象.关键字new后跟随一个函数调用,这个函数称为构造函数(constructor),构造函数用以初始化一个新创建的对象.
var o = new Object();
var a = new Array();
var b = new Date();
var c = new RegExp("js")
3.原型
每一个除null外的对象都与另一个对象相关联,另一个对象就叫做原型.
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Object.prototype获得原型对象的引用.通过关键字new创建的对象的原型就是构造函数的prototype属性的值,所以,通过new Object()创造的对象原型就是Object.prototype,数组的就是Array.prototype,日期的就是Date.prototype.
Object.prototype是没有原型的,Date等构造函数都有继承自Object.prototype的原型,如Date.prototype的属性继承自Object.prototype
4.Object.create()
Object.create()将创建一个新对象,第一个参数是这个对象的原型,第二个参数用以对对象的属性进行进一步描述
可以用Object.create(Object.prototype)来创建一个新的空对象
function inherit(p){
if(p===null) throw TypeError();
var t = typeof p;
if(t!=='object' && t!=='function') throw TypeError();
function f(){};
f.prototype = p;
return new f();
}
属性的查询和设置
var name = author.name;
var name = author["name"]
以上两种方法进行查询,也可直接设置属性
第二种方式看起来更像数组,但索引变成了字符串索引,这种数组就是所说的关联数组,也叫散列,映射或字典,JavaScript对象都是关联数组.
假如要查找一个对象的属性,如果当前对象不存在,则会在对象的原型对象中查询,还不存在就接着往上查询直到找到这个属性或者查找到一个原型是null的对象为止
在JavaScript中,只有查询才会体会到继承的存在,而设置属性与继承无关
属性的删除
delete运算符可以删除对象的属性,但delete不可以删除那些可配置性为false的属性(比如变量声明和函数声明的全局函数)
o = {x:1};
delete o.x;//删除成功
delete o.x;//不存在,什么也不做 返回true
delete o.toString;//同上
检测属性
判断某个属性是否存在于某个对象中,可以通过in运算符、hasOwnProperty()和propertyIsEnumerable()方法实现.
var o = {x:1};
"x" in o; //ture
"y" in o; //false
"toString" in o //true,继承
hasOwnProperty()用于检测给定的名字是否是对象的自有属性.但对于继承属性返回false
var o = {x:1};
o.hasOwnProperty("x"); //true
o.hasOwnProperty("y"); //false
o.hasOwnProperty("toString") //false
propertyIsEnumerable()相当于hasOwnProperty()的增强版,它只检测是自由属性请可枚举的的属性
除了这些方法还可以用!来判断,比如o.x ! undefined
枚举属性
之前提到的for/in循环遍历对象中所有可枚举的属性(包括自有属性和继承属性),把属性名称赋值给循环变量.所有为了避免继承的影响,通常会这样
for(p in o){
if(!o.hasOwnProperty(p)) continue; //跳过继承的属性
}
for(p in o){
if(typeof o[p] === "function") continue //跳过方法
}
此外还有Object.keys(),它返回一个数组,这个数组由可枚举的自有属性的名称组成,还有一个枚举属性的函数是Object.hasOwnPropertyNames(),它和上一个函数类似,但它返回对象的所有自有属性的名称
setter和getter
对象的属性值可以有一两个方法替代,这两个方法就是getter和setter,由这两个方法定义的属性就是"存取器属性"
var o = {
data_prop: value,//普通属性
get accessor_prop(){},
set accessor_prop(value){}
}
属性的特性
属性除了包含名字和值外,还包含一些可写,可枚举,可配置的特性.一般可以把数据属性看成是一个名字和四个特性,数据属性的四个特性是它的值,可写性,可枚举性和可配置性.存取器属性不具有值特性和可写性,它的可写性由setter存在与否决定,因此存取器属性的四个特性是读取,写入,可枚举,可配置.
通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符
Object.getOwnPropertyDescriptor({x:1},"x")
//返回 {value:1,writable:true,enumerable:true,configurable:true}
Object.getOwnPropertyDescriptor(random,"octet")
//返回 {setter:undefined,getter:/*func*/,enumerable:true,configurable:true}
Object.getOwnPropertyDescriptor({},"x")
//不存在,返回undefined
如果想要设置属性特性,或想让新建属性具有某特性,则要调用Object.defineProperty(),传入要修改的对象,要创建或修改的属性的名称以及属性描述符对象
var o = {};
Object.defineProperty(o,"x",{
value: 1,
writable: true,
enumerable: false,//不可枚举
configurable: true
})
o.x; //1
Object.keys(o); //[]不可枚举
Object.defineProperty(o,"x",{writable: false})//无法修改值
Object.defineProperty(o,"x",{get: function(){return 0}})
//将x从数据属性修改成存取器属性
o.x; //0
对象的三个属性
原型属性
对象的原型属性是用来继承属性的,简称为原型.原型属性是在实例对象创建之初就设置好的,
通过对象直接量创建的对象使用Object.prototype做为原型,通过new函数创建的对象则用构造函数的prototype属性作为原型,前两项其实都是构造函数constructor属性的prototype作为原型,Object.create()使用传入的参数作为原型
想检测一个对象是否是另一个对象的原型可以用isPrototypeOf()或者instanceOf()方法检测
类属性
对象的类属性是一个字符串,用以表示对象的类型信息.
通过内置构造函数创建的对象包含类属性(class attribute),它与构造函数名称相匹配,通过对象直接量和Object.create()传教的对象的类属性是object,自定义构造函数创建的对象类属性也是object
可拓展性
对象的可拓展性用以表示是否可以给对象添加新属性.所有内置对象和自定义对象都是显式可拓展的,宿主对象的可拓展性由JavaScript引擎定义.
通过将对象传入Object.esExtensible()来判断对象是否可拓展,如果想将对象转换成不可拓展,需要调用Object.preventExtensions()
Object.seal()将对象设置为不可拓展并且把所有自有属性设置为不可配置,可用Object.isSealed()来检测对象是否封闭
Object.freeze()将对象冻结,除了设置不可拓展和属性设置不可配置外,还将自有的所有数据属性设置为只读,使用Object.isFrozen()判断是否冻结
序列化对象
对象序列化是指将对象的状态转换成字符串,也可将字符串还原为对象
可以使用JSON.stringfy()和JSON.parse()来序列化和还原对象.
o = {x:1,y:{z:[false,null,""]}};
s = JSON.stringify(o);//转换成字符串 '{"x":1,"y":{"z":[false,null,""]}}'
p = JSON.parse(s);//p是o的深拷贝
JSON(JavaScript object notation)JavaScript对象表示法,JSON的语法是JavaScript语法的子集,支持对象,数组,字符串,无穷大数字,布尔值和null,并且可以序列化和还原,NaN,infinity的序列化结果是null,日期对象序列化是ISO格式的日期字符串,但JSON.parse()并不会还原成原始日期对象,函数,RegExp,Error对象和undefined值不能序列化和还原
对象方法
-
toString()
该方法没有参数,它返回一个表示调用这个方法的对象值的字符串.在需要变成字符串的时候,JavaScript会调用这个方法,比如+
-
toLocaleString()
除了基本的toString()外,对象都包含toLocaleString()方法,这个方法返回一个表示这个对象的本地化字符串,object中默认的toLocaleString()不返回任何本地化自身的操作,仅调用自身的toString(),Date和Number类对toLocalString()方法做了定制,可以用它对数字,日期和时间进行本地化转换.数组调用则会让每个数组元素调用toLocalString()方法转换成字符串
- toJSON()
对于需要执行序列化的对象来说,JSON.stringify()方法会调用toJSON()方法.
- valueOf()
该方法和toString()方法很像,但往往当JavaScript需要将对象转换为某种原始值而非字符串才会调用它,尤其是转换为数字,如果在需要使用原始值的上下文中使用了对象,JavaScript就会自动调用这个方法