对象
6.1 创建对象
创建对象的方法:对象直接量、关键字new和Object.create()
6.1.1 对象直接量
属性名在不是保留字、没有空格和连接符等情况下可以没有引号。
var empty={} //没有属性的对象
var point={x:0, y:1} //两个属性的普通对象
var book={
"main title" : "JS", //属性名有空格,必须用字符串
"sub-title" : "Guide", //属性名有连接符,必须用字符串
"for" : "all", //属性名是保留字,必须用字符串
author:{ //属性值是一个对象
firstname: "Joe", //属性名可以没有引号
surname: "Flanagan"
}
}
6.1.2 通过new创建对象
new运算符创建并初始化一个新对象。关键字new后跟随一个函数调用,这个函数成为构造函数(construcor),用来初始化一个新建的对象。
Javascript语言核心中的原始类型都包含内置构造函数:
var o = new Object(); // 创建一个空对象,和{}一样
var a = new Array(); // 创建一个空数组,和[]一样
var d = new Date(); // 创建一个表示当前时间的Date对象
用自定义构造函数来初始化新对象也很常见。
6.1.3 原型
每一个Javascript对象(null除外)都和另一个对象相关联。“另一个”对象就是原型,每个对象都从原型继承属性。
对象创建方式 | 原型 | 备注 |
---|---|---|
对象直接量 | Object.prototype | |
关键字new和构造函数 | 构造函数的prototype | 通过new Object()创建,原型为Object.prototype 通过new Array()创建,原型为Array.prototype 通过new Date()创建,原型为Date.prototype |
Object.prototype没有原型,像Date.prototype继承自Object.prototype。所以具有以下关系:
new Date()创建的对象 继承自 -> Date.prototype 继承自 -> Object.prototype
这一系列链接的原型对象就是所谓的“原型链”(prototype chain)。
6.1.4 Object.create()
方法 | 用途 | 参数1 | 参数2(可选) |
---|---|---|---|
Object.create() | 创建一个新对象 | 这个对象的原型 | 对对象的属性进行进一步描述 |
var obj1=Object.create({x:1, y:2}); //obj1继承了属性x和y
//创建一个空对象
var obj2=Object.create(Object.prototype); //和{}及new Object()一样
6.2 属性的查询和设置
通过.
和[]
可以获取和设置属性值。
var obj={};
obj.x=1; //设置属性
obj["y"]=2; //设置属性
obj.y //=> 2: 查询属性
obj["x"] //=> 1: 查询属性
注意,.
后面不能是保留字,obj.for
是非法的。只能用[]
,例如obj["for"]
。
6.2.2 继承
Javascript对象具有“自有属性”(own property),也有一些属性是从原型对象继承而来的。属性的查找沿着原型链一直向上。
6.2.3 属性访问错误
如果在对象自身的属性和继承的属性中都没有找到某个属性,则返回undefined。
var obj = {};
obj.x; //=> undefined
obj.x.length //报错
对象为null,或者查询null和undefined的属性,都会报错。
var obj=null;
obj.x; //报错
常用下面的方式来避免报错:
var len= obj && obj.x && obj.x.length;
内置构造函数的原型是只读的。
Object.prototype = 0; //赋值失败,但不报错
6.3 删除属性
delete运算符可以删除属性对象。需要注意,delete只是断开属性和宿主对象的联系,而不会操作属性中的属性。
var obj={ p:{x:1} };
var b = obj.p;
delete obj.p;
obj.p; //=> undefined
b.x; //=> 1
所以在销毁对象时,要遍历属性中的属性,依次删除,否则会造成内存泄露。
成功删除或没有任何副作用(比如删除不存在的属性)返回true。不能删除可配置性为false的属性。
delete 1; //=> true
delete Object.prototype; //=> false
6.4 检测属性
检测某个属性是否在某个对象中,有几种方式:
检测方式 | 说明 | 备注 |
---|---|---|
!== 运算符(不推荐) | 检测自有和继承属性 | 判断属性是否是undefined |
in运算符 | 检测自有和继承属性 | 4.9.3中介绍过,比起!==可以区分是不存在的属性,还是存在但值为undefined |
hasOwnProperty() | 仅检测自有属性 | |
propertyIsENumerable() | 仅检测自有属性且该属性可枚举 | 某些内置属性不可枚举,通常由Javascript代码创建的属性都是可枚举的。 |
var obj = {x:1};
obj.x !== undefined //=> true
obj.toString !== undefined //=> true: 继承属性
"x" in obj; //=> true
"toString" in obj //=> true: 继承属性
obj.hasOwnProperty("x") //=> true
obj.hasOwnProperty("toString") //=> false: 继承属性
Object.prototype.propertyIsEnumerable("toString") //=> false: 不可枚举
6.5 枚举属性
遍历对象的属性,有以下几种方式:
方式 | 说明 |
---|---|
for/in循环遍历 | |
Object.keys() | 返回一个数组,由对象中可枚举的自有属性的名称组成。 |
Object.getOwnPropertyNames() | 返回一个数组,由对象中所有自有属性,不仅仅是可枚举的属性的名称组成。 |
var obj={x:2,y:3};
for(att in obj){
console.log(att); //=> x y
}
Object.keys(obj) //=> ["x","y"]
Object.keys(Object.prototype) //=> []
Object.getOwnPropertyNames(obj) //=> ["x","y"]
Object.getOwnPropertyNames(Object.prototype) //=> ["constructor", "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "__proto__", "toLocaleString"]
6.6 属性getter和setter
属性值可以用一个或两个方法(getter和setter)代替。由getter和setter定义的属性称作“存取器属性”(accessories property),它不同于“数据属性”(data property)——只有一个简单的值。
var p={
x:1,
y:2,
get sum(){
return this.x+this.y;
},
set sum(base){
this.x += base;
this.y += base;
}
}
p.x //=> 1
p.sum //=> 3
p.sum = 5
p.x //=> 6
p.sum //=> 13
和数据属性一样,存取器属性是可以继承的。
6.7 属性的特性
一个属性包含1个名称和4个特性:
属性分类 | 特有特性 | 共有特性 |
---|---|---|
数据属性 | 值(value)、可写性(writable) | 可枚举性(enumerable)、可配置性(configurable) |
存取器属性 | 读取(get)、写入(set) | 同上 |
为实现属性特性的查询和设置。ECMAScript 5定义了一个名为“属性描述符”(property descriptor)的对象来代表那4个特性。
6.7.1 查询属性描述符
调用Objet.getOwnPropertyDescriptor()
获得某个对象特定属性的自有属性描述符。
configurable: true}
Object.getOwnPropertyDescriptor({x:1},"x") //=> {value: 1, writable: true, enumerable: true,
//6.6中的p对象
Object.getOwnPropertyDescriptor(p,"sum") //=> {get: ƒ, set: ƒ, enumerable: true, configurable: true}
//继承属性和不存在的属性,返回undefined
Object.getOwnPropertyDescriptor({},"x") //=> undefined
Object.getOwnPropertyDescriptor({x:1},"toString") //=> undefined
6.7.2 设置单个属性描述符
设置属性特性或想让新建属性具有某种特性,需要调用Object.defineProperty()
。注意,不能修改继承属性。
参数: 要修改的对象;创建或修改的属性名称;属性描述符对象。
var obj={};
//添加一个不可枚举的数据属性x,并赋值为1
Object.defineProperty(obj,"x",{ value: 1,
writable: true,
enumerable: false,
configurable: true});
//属性存在,但不可枚举
obj.x //=> 1
Object.keys(obj) //=> []
//修改x属性为只读
Object.defineProperty(obj,"x",{writable: false});
obj.x = 2; //操作失败但不会报错
obj.x //=> 1
//可以通过配置修改值
Object.defineProperty(obj,"x",{value: 2});
obj.x //=> 2
//将x从数据属性修改为存取器属性
Object.defineProperty(obj,"x",{get: function(){return 0;}});
obj.x //=> 0
6.7.3 设置多个属性描述符
如果需要同时修改或创建多个属性,使用Object.defineProperties()
。
参数:要修改的对象;参数映射表
var p = Object.defineProperties({},{
x:{value:1, writable:true, enumerable:true, configurable:true},
y:{value:2, writable:true, enumerable:true, configurable:true},
sum:{
get: function(){ return this.x+this.y},
enumerable:true,
configurable:true
}
})
6.8 对象的三个属性
每个对象都有与之相关的原型(prototype)、类(class)和可扩展性(extensible attribute)。
6.8.1 原型属性
对象的原型属性是用来继承属性的,经常把“o的原型属性”直接叫做“o的原型”。
要检测一个对象是否是另一个对象的原型(或处于原型链中),可以使用isPrototypeOf()
方法,看下面两个例子:
var p = {x:1};
var obj = Object.create(p);
p.isPrototypeOf(obj); //=> true
var d = new Date();
Date.prototype.isPrototypeOf(d); //=>true
Object.prototype.isPrototypeOf(d) //=>true: 原型链中
6.8.2 类属性
对象的类属性(class attribute)是一个字符串,用以表示对象的类型信息。
调用对象的toString()方法,提取返回字符串的第8到倒数第2个位置之间的字符就可以获得对象的类,下面这个函数实现了这个功能:
//获取类属性的函数
function classof(o){
if(o===null) return "Null";
if(o===undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
//---测试这个函数,进行类属性的获取---
classof(null) //=> "Null"
classof(false) //=> "Boolean"
classof(new Date()) //=> "Date"
function f(){} //定义一个自定义构造函数
classof(f) //=> "Function"
classof(new f()) //=> "Object"
6.8.3 可扩展性
对象的可扩展性用以表示是否可以给对象添加新属性。
方法 | 作用 | 方法返回 | 备注 |
---|---|---|---|
Object.preventExtensions() | 将对象转换为不可扩展 | 传入对象 | 一旦将对象转换为不可扩展,将无法将其转换回可扩展 |
Object.seal() | 将对象转换为不可扩展,且所有自有属性不可配置 | 同上 | 已经封闭(sealed)的对象不能解封。不能给对象添加新属性,已有属性不能删除和配置,除了已有的可写属性可以设置。 |
Object.freeze() | 冻结对象,对象不可以扩展,自有属性不可配置且为只读 | 同上 | 如果对象存取器属性具有setter方法,则该属性赋值依然可用 |
Object.isExtensible() | 检测对象是否可扩展 | 布尔型 | 在ECMAScript5中,所有内置对象和自定义对象都是可以扩展的。 |
Object.isSealed() | 检测对象是否封闭 | 同上 | - |
Object.isFrozen() | 检测对象是否冻结 | 同上 | - |
var obj={x:1};
Object.isExtensible(obj); //=> true
Object.preventExtensions(obj); //=> {x: 1}
Object.isExtensible(obj); //=> false
obj.y=2;
obj.y //=> undefined: 说明不可扩展
delete obj.x;
obj.x //=> undefined: 属性可以被删除
var obj = Object.defineProperties({},{
x:{value:1, writable:true, configurable: true},
y:{value:2, writable:false, configurable: true},
});
//6.7.2中讲到,不可读的属性可以通过配置进行修改,而封锁之后不可修改了
Object.defineProperty(obj,"y",{value: 200}); //=> {x: 1, y: 200}
Object.seal(obj); //=> {x: 1, y: 200}: 封锁
Object.defineProperty(obj,"y",{value: 300}); //=> Uncaught TypeError
Object.defineProperty(obj,"x",{value: 100}); //=> {x: 100, y: 200}: 由于x是可写属性,依旧可以修改
delete obj.x //=> false: 不可删除
Object.isSealed(obj); //=> true: 已经封锁
var obj = Object.defineProperties({},{
x:{value:1, writable:true, configurable: true},
y:{value:2, writable:false, configurable: true},
});
Object.freeze(obj); //=> {x: 1, y: 2}: 冻结
Object.defineProperty(obj,"x",{value: 100}); //=> Uncaught TypeError: 所有属性只读
Object.isFrozen(obj); //=> true: 已经冻结
6.9 序列化对象
对象序列化(serialization)是指对象和字符串间相互转换。
6.9.1 认识JSON
JSON: **J**ava**S**cript **O**bject **N**otation(JavaScript对象表示法),是轻量级的文本数据交换格式。
如果一段字符串符合JSON的语法,就可以将其简称为“一段JSON”。
注意,Javascript中的对象的名可以加单引号、双引号或者不加引号,而JSON必须使用双引号。
关于JSON的详情,请参考:http://www.json.org.cn/。
JSON在线编辑(校验、格式化):
http://www.json.org.cn/tools/JSONEditorOnline2.0/index.htm
6.9.2 JSON和JS对象的转换
函数 | 作用 |
---|---|
JSON.stringify() | 对象 –> JSON |
JSON.parse() | JSON –> 对象 |
var obj={x:1,y:2}
var myJSON = JSON.stringify(obj);
myJSON //=> "{"x":1,"y":2}"
var myObj = JSON.parse(myJSON);
myObj //=> {x: 1, y: 2}