理解对象
对象是JavaScript的引用数据类型,可以看作是属性的无序集合,每个属性都是一个名/值对。属性名为字符串(属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性),值可以是任意JavaScript值,或者是一个getter或setter函数(或两者都有),但是对象不仅仅是字符串到值的映射,除了拥有自有属性,还可以从一个被称作原型的对象中继承属性。对象的方法通常是继承的属性。
对象最常见的方法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)和枚举(enumerate)它的属性。
从属性角度理解对象时,对象拥有自有属性和继承属性,其中自有属性是直接在对象中定义的属性,而继承属性是继承了在对象原型中对象定义的属性。
从构造函数角度理解时,对象体系是基于构造函数和原型的。其中可以在构造函数中自定义属性,同时可以从原型上继承其属性。
对象类型
- 内置对象(native object):由ECMAScript规范定义的对象或类,例如数组、函数、日期、正则表达式
- 宿主对象(host object):是由JavaScript解释器所嵌入的宿主环境(比如Web浏览器)定义的,例如BOM、DOM
- 自定义对象(user-defined object):是由运行中的JavaScript代码创建的对象
创建对象
对象直接量(对象字面量)
对象直面量是由若干名/值对组成的映射表。属性的值可以是任意类型的JavaScript表达式,表达式的值就是这个属性的值。
var point = {a:1,b:2};
var book={
"main title":"JavaScript",//属性名存在空格或连字符(-)需用字符串表示
"for":"all audiens", //for为关键字,属性名需加引号
author:{ //属性值还可以为一个对象
firstname:"Alice",
surname:"Flangan"
}
}
对象直接量是一个表达式,该表达式每次运算都会创建并初始化一个新的对象,这个对象拥有自己定义的属性以及继承的原型对象属性,具体示例如下图所示:
关键字new
new运算符创建并初始化一个新对象。关键字new后接一个函数调用,该函数为构造函数(constructor),构造函数用以初始化一个新创建的对象。
var o = new Object();
JavaScript原生提供Object对象,其他对象都继承自这个对象,Object本身是一个构造函数,可直接通过它生成新的对象。
其中
new命令原理:
- 创建一个空对象(
{}) - 给新创建的对象添加属性
_proto_,将该属性链接至构造函数的原型对象 - 新对象赋值给构造函数内部的
this上下文 - 如果构造函数没有显示返回对象,则返回
this(及生成的实例)
PS:每个构造函数都有一个prototype属性指向原型对象,用来存放共有属性和方法的地址
PPS:每个实例对象都有一个_proto_属性指向构造函数的原型对象
Object.create()
ES5中定义了一个Object.create( )方法来创建一个新对象,其包含两个参数,第一个参数为这个对象的原型,第二个参数用来对对象的属性进行描述。因此通过该方法创建的对象可以指定其原型。
Ps:Object.create()是一个静态函数,而不是提供给某个对象调用的方法,给其传入所需的原型对象即可使用。
var o1 = Object.create({x:1,y:2});
Ps:可以通过传入参数null来创建一个没有原型的新对象,但是该对象不会继承任何东西,如果需要创建一个普通的空对象,需要传入Object.prototype
var o2 = Object.create(Object.prototype);
传入null后如图所示:
属性
属性的查询和设置
object.property
object["property"]
通过点.和方括号[]可以查询获取属性的值,其同样可以创造属性或者给属性赋值。
属性的赋值操作会先检查原型链,从而判断是否可以进行赋值操作,可以进行赋值时,也只是在对象上创建属性或者给已有属性赋值,不会修改原型链。查询属性时会涉及到继承,而设置属性时不会,因此可以有选择的覆盖继承的属性。
PS:对于点.来说,右侧需连接以属性名称命名的简单标识符;对于方括号[]来说,方括号内必须为一个计算结果为字符串的表达式(严格来讲必须返回字符串或者返回一个可以转换为字符串的值)
PPS:概括理解就是.后面只能接属性名,不能接变量,[ ]的话就可以
属性访问错误
查询一个不存在的属性不会报错,会返回undefined,如果查询一个不存在的对象的属性会抛出类型错误异常
删除属性
delete运算符可以删除对象的属性,其操作数应该是一个属性访问表达式。当delete表达式删除成功或没有任何副作用时返回true。
PS:delete运算只是断开属性与宿主对象之间的联系,不会操作属性中的属性;delete只能删除自有属性,不能删除继承属性,delete也不能删除哪些可配置性为false的属性(例如Object.prototype),也不能删除声明的全部变量和全局函数。
检测属性
通过in运算符、hasOwnPreperty()和propertyIsEnumerable()方法或者属性查询来判断某个属性是否存在某个对象中。
in
in运算符左侧是属性名(字符串),右侧是对象,判断对象自有属性或者继承属性时返回true;
hasOwnPreperty()
对象的hasOwnPreperty()方法检测对象的自有属性,对于继承属性会返回false;
propertyIsEnumerable()
propertyIsEnumerable()作为hasOwnPreperty()的增强版,只有检测到自有属性且该属性的可枚举性为true时它在返回true。
枚举属性
for/in
通过for/in循环和ES5提供的Object.keys()和Object.getOwnPropertyNames()可以实现对象属性的遍历。
for(variable in object)
statement
该循环在循环体中遍历对象所有可枚举的属性(包括自有属性和继承属性)
Object.keys()
返回一个由对象中可枚举的自有属性名称组成的数组
Object.getOwnPropertyNames()
返回一个对象的所有自有属性名称的数组
⭐️属性getter和setter
ES5中属性值可以用getter和setter方法替代,由getter和setter定义的属性称做存取器属性(accessor property)其不同于数据属性(data property),数据属性只有一个简单的值。
get语法将对象属性绑定到查询该属性时将被调用的函数,调用getter方法(无参数)可查询存取器属性的值,其返回值为属性存储表达式的值;当需要允许访问返回动态计算值的属性或者反映内部变量的状态时,可以使用getter实现;
set语法将对象属性绑定到要调用的函数,用setter方法可以设置一个存储器属性的值,其将赋值表达式右边的值作为参数传入setter。
定义存储器属性及使用示例:
var o = {
data_prop = value,//普通数据属性
//存储器属性都是成对定义的函数
get accessor_prop() {
/*这里是函数体*/
},
set accessor_prop(value) {
/*这里是函数体*/
}
}
//使用示例
var o = {
a:2, //普通数据属性
//存储器属性都是成对定义的函数
get accessor_prop() {
/*这里是函数体*/
return this.a
},
set accessor_prop(val) {
/*这里是函数体*/
this.a:val
}
}
o.accessor_prop = 3
console.log(o.accessor_prop) //4
console.log(o.a) //3
属性的特性
数据属性的4个特性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)、可配置性(configurable)。存取器属性的4个特性分别是读取(get)、写入(set)、可枚举性、可配置性,其不具有值特性和可写性,其可写性由setter方法存在与否决定的。
- 可写(writable attribute)表明是否可以设置该属性的值
- 可枚举(enumerable attribute)表明是否可以通过
for/in循环返回该属性 - 可配置(configurable attribute)表明是否可以删除或修改该属性
ES5定义了一个属性描述符(property descriptor)对象,用来实现属性特性的查询和设置操作。数据属性的描述符对象的属性和他们所描述属性特性是同名的,其中writable、enumerable、configurable都是布尔值,get属性和set属性是函数值。
通过调用Object.getOwnPropertyDecriptor()可以获得某个对象特定属性的属性描述符:
Object.getOwnPropertyDecriptor({x:1},"x");
//返{value:1,writable:true,enumerable:true,configurable:true}
PS:对于继承属性和不存在的属性会返回undefined,如果想获得继承属性的特性需要遍历原型链(Object.getPrototypeOf());想设置属性的特性或者想让新建属性具有某种特性,需要调用Object.defineProperty(),传入要修改的对象、要创建或修改的属性的名称以及属性描述对象;想要同时修改或创建多个属性,则需要使用Object.definePropertys(),第一个参数为需要修改的对象,第二个参数是一个映射表,其包括要新建或修改的属性名称,以及他们的属性描述符。
序列化对象
序列化对象(serialization)是指将对象的状态转换为字符串,也可将字符串还原为对象。
ES5提供了内置函数JSON.stringify()和JSON.parse() 来序列化和还原对象,其都使用JSON(JavaScript Object Notation——JavaScript对象表示法)作为数据交换格式。
o={x:1,y:{z:[false,null,""]}};
s=JSON.stringify(o); //'{x:1,y:{z:[false,null,""]}}'
p=JSON.parse(s); //p是o的深拷贝
PS:函数、RegExp、Error对象和undefined值不能序列化和还原,JSON.stringfy()只能序列化对象可枚举的自有属性。
常用对象方法
通过对象字面量和关键字new创建的对象都是从Object.prototype继承属性,这些继承属性主要是方法
toString()
该方法将返回一个表示调用这个方法的对象值的字符串
toString()常常会被重写,因此需要使用Object.prototype.toString.call(),通过call指定各参数为Object.prototype对象中的toString方法的上下文,Object.prototype对象上的toString方法可以用来判断数据类型
valueOf()
该方法与tostring()类似,主要在将对象转换为某种原始值而非字符串时调用(尤其是转换为数字的时候),返回指定对象的原始值
toString()和valueOf()的主要区别
二者并存的情况下,在数值运算中,优先调用valueOf,字符串运算中,优先调用toString。