对象
对象是JavaScript的基本数据类型:属性的无序集合。每个属性key: value
和属性描述符descripter
组成。
属性名
key
:字符串或合法的变量标识符;属性值
value
:可以是任意JavaScript值(number
、string
、boolean
、null
、undefined
和数组、对象).value
可以是getter
和setter
。属性描述符
descripter
:每个属性的三个相关特性writable
、enumerable
、configurable
,其值都是布尔类型,默认都为true
1 创建对象
创建对象有三种方法:对象字面量{a: 1}
,new Constructor()
、Object.create()
1.1 对象字面量
var book = {
"main title": "JavaScript", //有空格或其他非标识符、关键字,必须使用双引号
"sub-title": "The Definitive Guide",
"for": "all audiences",
author: {
firstname: "David",
lastname: "Flanagan"
}
}
1.2 new
操作符调用构造函数
可以使用new
操作符调用一个构造器函数Constructor()
创建一个对象。
var o = new String();
var d = new Date();
1.3 Object.create()
方法
原型:每个JS对象都有一个与之相关联的原型对象prototype
,对象其原型对象上继承属性。(当前对象中的__proto__
属性是其原型对象的链接)
所有通过字面量创建的对象的原型都是Object.prototype
,一个字面量对象{}
相当于调用new Object()
。
Object.prototype
的原型是null
,所以null
是原型的出口。
Object.create()
是一个静态函数(不是提供给对象调用的方法),用于创建对象:
第一个参数:原型对象,创建的新对象以该参数为原型;
第二个参数:可选,用于对对象属性的描述
创建一个没有原型的对象:
Object.create(null);
创建一个普通空对象(
{}
或new Object()
):Object.create(Object.prototype);
2 对象三个特殊属性
每个对象都有与之相关的3个属性:原型prototype
、类属性calss attribute
、可扩展性extensible attribute
2.1 原型
每个JS对象都与另一个原型对象(prototype
),利用原型可以实现继承。
字面量的原型是:
Object.prototype
new
操作符创建的对象其原型:构造器函数的原型(prototype
属性)Object.create()
创建的对象,其原型:第一个参数
查询对象的原型
ES5中定义了
Object.getPrototypeOf()
函数,来查询对象的原型利用
new
创建的对象会继承constructor
属性,指向创建该对的构造器函数,所有该对象的原型是:obj.constructor.prototype
字面量
{}
的constructor
属性指向Object()
-
使用
isPrototypeOf()
方法查看一个对象是否为另一个对象的原型(或处于原型链中)Array.prototype.isPrototypeOf([]); // ==> true
2.2 类属性
对象的类属性class attribute
是一个字符串,用来表示对象的信息。ES3与ES5均为提供设置类属性的API,只能通过继承自Object.prototype
的toString()
方法来简洁查询。
{}.toString(); // ==> "[object Object]"
通过对返回字符串切片:第8位到倒数第2位即可获得对象的类型。但是许多对象重写了toString()
方法,需要间接调用Function
的call()
方法
function classof(o) {
if(o === null) {return "Null";}
if(o === undefined) {return "Undefined"}
return Object.prototype.toString.call(o).slice(8, -1);
}
classof()
方法可以返回传递给它的任意对象的类型,参数为number
、string
、boolean
时,使用包装类型将其转化为对象,再进行操作。
classof(null); // ==> "Null"
classof(2); // ==> "Number"
classof(false); // ==> "Boolean"
classof(new Date()); // ==> "Date"
classof(""); // ==> "String"
classof({}); // ==> "Object"
classof([]); // ==> "Array"
classof(/./); // ==> "RegExp"
classof(window); // ==> "Window",宿主对象
function F() {}; //自定义一个构造器函数
classof(new F()); // ==> "Object"
2.3 可扩展性extensible attribute
对象的课扩展性用来描述对象是否可以添加新的属性。所有的内置对象和自定义对象都是可扩展的,除非将其转化为不可扩展
Object.esExtensible()
函数判断传入对象是否可以扩展;Object.preventExensible()
函数将传入的对象设置为不可扩展,并且过程不可逆;只影响对象本身的可扩展性Object.seal()
函数将对象设置为不可扩展,属性描述符configurable
设置为false
(不能添加新属性,已有的属性不能删除或配置,但是对于writable: true
时,可以修改属性的值);过程不可逆Object.isSealed()
函数判断一个对象是否封闭Object.freeze()
函数将对象设置为不可扩展,属性描述符configurable: false;
,writable: false;
只读。value
为accessor property
含有setter
函数,不受影响
3 组成对象的属性
每个对象是无需的属性集合,属性名可以是合法的变量标识符或字符串;变量值可以是任意JavaScript值;属性由描述符descripter
来控制特性;
3.1 属性的查询与设置
属性可以通过.
和[]
来访问:
使用
.
访问时,其右侧必须是属性名称命名的简单标识符-
使用
[]
访问时,括号内必须是计算结果为字符串的表达式,字符串是属性的名字。变量名为关键字、含有空格或属性名是变量时,必须使用[]
var author = book.author; var name = author.surname; var title = book["main title"];
属性的设置:与访问相似,将其放在赋值表达式的左侧
book.edtion = 6;
book["main title"] = "ECMAScript";
继承与错误
在查询一个对象是否存在时,先查看自身属性,如果没有;通过原型链逐层向上查找,直到原型链顶端null
为止。
如果自身属性与原型链中均为找到,属性访问返回
undefined
,不报错如果查询不存在对象的属性,会报错
为
null
和undefined
设置属性会报错
3.2 属性删除
使用delete
操作符,可以删除对象的属性(其描述符中configurable
为true
),并且只能删除自有属性,不能删除继承属性
3.3 属性检测
判断某个属性是否在某个对象中,JS有三种方法:in
、hasOwnProperty()
、propertyIsEnumerable()
in
:如果对象自身属性或继承属性中包含该属性,返回true;hasOwnProperty()
:只有对象自身属性包含该属性时,才返回true
-
propertyIsEnumerable()
:只有对象自身属性包含该属性,并且该属性是可以枚举(描述符中enumerable: true;
)var o = {a : 1}; "a" in o; // ==> true o.hasOwnProperty("a"); // ==> true o.propertyIsEnumerable("a"); // ==> true
3.4 枚举属性
使用for-in
循环可以遍历对象中所有可枚举的属性(包括自身属性与继承属性),把属性名赋值给循环变量。ES5定义了Object.keys()
和Object.getOwnPropertyNames()
两个函数用来枚举属性名称。
Object.keys()
:返回对象中可枚举的自有属性名组成的数组Object.getOwnPropertyNames()
:返回对象所有的自有属性名组成的数组,包括不可枚举的属性
对象继承的内置方法都是不可枚举的,给对象中增加的属性都可以枚举(除非设置其enumerable: false
)
有许多使用工具库为Object.prototype
增加新的属性,这些属性可以被所有对象继承使用,但是在ES5之前,不能将其设置为不可枚举,所以在for-in
循环时会被枚举出来。
//过滤继承的属性
for(p in o) {
if(!o.hasOwnProperty(p)) {continue;}
}
//跳过方法
for(p in o) {
if(typeof p === "function") {continue;}
}
操作对象属性的工具函数:
/**
* 枚举属性的工具函数
* 将对象p中可枚举的属性复制到对象o中,返回对象o;
* 如果p和o含有同名的属性,则覆盖o中的属性
* 不处理getter和setter以及复制属性
*/
function extend(o, p) {
for(let prop in p) {
o[prop] = p[prop];
}
return o;
}
/**
* 将对象p中可枚举的属性复制到对象o中,返回对象o;
* 如果o和p有同名属性,不影响o中的属性
* 不处理getter和setter以及复制属性
*/
function merge(o, p) {
for(let prop in p) {
if(o.hasOwnProperty(prop)) {
continue;
}
o[prop] = p[prop];
}
return o;
}
/**
* 如果o中的属性在p中没有同名属性,从o中删除这个属性,并返回o
*/
function restrict(o, p) {
for(let prop in o) {
if(!(prop in p)) {
delete o[prop];
}
}
return o;
}
/**
* 如果o中的属性在p中存在同名属性,从o中删除这个属性,并返回o
*/
function substrict(o, p) {
for(let prop in o) {
if(prop in p) {
delete o[prop];
}
}
return o;
}
/**
* 返回一个数组,包含o中可枚举的自有属性的名字
*/
function keys(o) {
//o必须是对象
if(typeof o !== 'object') {
throw TypeError();
}
var result = [];
for(let prop in o) {
if(o.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
}
3.5 属性setter
和getter
属性的组成:key
、value
、descripter
。在ES5中,value
可以用一个或两个方法替代(getter
和setter
方法)
所有JavaScript的
value
叫做数据属性data property
getter
和setter
定义的属性叫做存取器属性:accessor property
不同于data property
,accessor property
的读写属性由getter
和setter
决定。
如果属性同时有
getter
和setter
,它是一个可读写的属性;如果属性只有
getter
,它是一个只读的属性;如果属性只有
setter
,它是一个只写的属性;读取只写属性总是返回undefined
getter
和setter
定义
var o = {
data_prop: value; //普通数据属性
//存取器属性是成对定义的函数,函数名是该属性名,没有冒号分隔函数体以属性名
get accessor_prop() {...},
set accessor_prop(value) {...}
}
var p = {
//x、y是普通可读写的数据属性
x: 1.0,
y: 1.0,
//r是可读写的存取器属性,具有getter和setter
//函数结束后要带上逗号
get r() {return Math.sqrt(this.x * this.x + this.y * this.y)},
set r(newvalue) {
var oldvalue = Math.sqrt(this.x * this.x + this.y * this.y);
var ratio = newvalue / oldvalue;
this.x *= ratio;
this.y *= ratio;
},
//theta是只读存取器属性,只有getter方法
get theta() {return Math.atan2(this.y, this.x)}
};
函数体内的
this
指向表示这个点的对象
3.6 属性的描述符descripter
属性除key
、value
,还有一组用于描述其特性的descripter
,其中有writable
、enumerable
和configurable
三个属性,其值都为布尔类型,默认为true
假设将setter
和getter
看做descripter
,同理将属性的value
也看做descripter
data property
的四个特性:value
、writable
、enumerable
、configurable
accessory property
的四个特性:getter
、setter
、enumerable
、configurable
ES5提供操作descripter
的API:
调用
Object.getOwnPropertyDescripter()
函数,获取某个对象中特定自有属性的descirpter
调用
Object.getPrototypeOf()
函数可以获取继承属性的descirpter
-
调用
Object.defineProperty()
函数可以设置属性的特性,或者新建属性,使其具有某种特性//返回{ value: 1, writable: true, enumerable: true, configurable: true } Object.getOwnPropertyDescriptor({a: 1}, "a"); //继承属性和不存在的属性,返回undefined Object.getOwnPropertyDescriptor({}); Object.getOwnPropertyDescriptor({}, "toString");
var o = {}; //创建空对象 //添加一个不可枚举的属性,其值为1 Object.defineProperty(o, "x", {value: 1, writable: true, enumerable: false, configurable: true}); o.x; //属性存在,但不可枚举, ==>1 Object.keys(o); // ==> [] //对x属性进行修改,使其变为只读 Object.defineProperty(o, 'x', {writable: false}); o.x = 2; //操作失败,但不报错;在严格模式下抛出类型错误 o.x; // ==> 1 //属性的值可以配置 Object.defineProperty(o, 'x', {value: 2}); o.x; // ==> 2 //将x从数据属性修改为存取器属性 Object.defineProperty(o, 'x', {get function() {return 0;}}); o.x; //==> 0
4 序列化对象
序列化对象指将对象的状态转化为字符串,同时可以将JSON
字符串转化为对象。ES5内置JSON
对象的JSON.stringgify()
和JSON.parse()
可以完成序列化和解析。
var o = {x: 1, y: {z: [false, null, ""]}}; //测试对象
var s =JSON.stringify(o); // '{"x":1,"y":{"z":[false,null,""]}}'
var p = JSON.parse(s); //p是s的深拷贝
JSON
是JavaScript的子集,不能表示JavaScript中所有值。
可以表示:
object
、Array
、string
、number
、boolean
、null
,可以序列化与还原。infinity
、NaN
序列化结果是null
,Date
对象序列化结果是日期字符串不能表示:函数、
RegExp
、Error
对象和undefined
JSON.stringify()
只能序列化对象自有的可枚举属性。
5 Object.prototype
对象的方法
hasOwnProperty()
:检测该属性是否为对象自有属性propertyIsEnumerable()
:检测该属性是否是对象自有,并且可枚举的属性isPrototypeOf
:检测某对象是否为另一个对象的原型toString()
:无参数,返回调用该方法对象值的字符串,由于功能有限,某些对象重写了该方法:Array.toString()
、Date.toString()
、Function.toString()
toLoaclString()
:Object
中的toLoaclString()
只是调用toString()
,只有Date
和Number
类对toLoaclString()
定制,可以对数字、日期和时间进行本地化处理
6 总结
JS中对象是属性的无序集合,每个对象有三个相关特性:原型
prototype
、类属性class property
、可扩展性extensible
对象的创建三种方法:对象字面量、
new
调用构造函数和Object.create()
-
每个属性由三个部分组成:
key
、value
和descripter
key
只能是字符串或者值为字符串的变量标识符value
可以是任意JavaScript值(数据属性data property
);可以是一个或两个方法,getter
和setter
(控制区属性accessor property
)descripter
:writable
、enumerable
、configurable
,其值都是布尔类型,默认都为true
。可以使用Object.defineProperty()
操作描述符特性。
writable
指该属性是否可以写入数据;enumerable
指该属性是否可以枚举,(使用for-in
循环可以列出,ES5提供Object.keys()
和Object.getOwnPropertyNames()
);configurable
指该属性是否可以用delete
操作符删除和配置。