JavaScript对象详解

对象

对象是一种复合值,汇聚多个值并允许我们按名字存储和获取这些值

对象可以创建对象字面量,new关键字和Object.create()函数来创建

对象字面量

let empty={}
let point={x:0,y:e}
let p2={x:point.x,y:point.y+1}

使用New创建对象

new 操作符用于创建和初始化一个新对象。new 关键字后面必须跟一个函数调用

let o=new Object() 创建一个空对象
let a=new Array()  创建一个空数组
let d=new Date()   创建一个表示当前时间的日期对象
let r=new Map()	   创建一个映射对象

原型

这另一个对象被称为原型,第一个对象从这个原型继承属性

通过对象字面量创建的所有对象都有相同的原型对象

Object.prototype引用这个原型对象,使用new关键字和构造函数调用创建的对象,使用构造函数prototype属性的值作为他们的原型

使用new关键字创建的对象继承Object.prototype

Object.Create

创建一个新对象,使用其第一个参数作为新对象的原型

let o1=Object.create({x:1,y:2})
o1.x+o1
//如果create(null)
如果传入null可以创建一个没有原型的新对象,不过,这样创建的新对象不会继承任何东西,连tostring方法都不存在
let o2=Object.tostring(null)

查询和设置属性

要获取一个属性的值,可以使用点(.)或方括号(【】)操作符。左边一个是一个表达式,值未一个对象

let m={m:1}
m["m"]=2

作为数组的关联对象

JavaScript对象是关联数组

在C C++ Java 强类型语言中,对象有固定数量的属性,并且这些属性的名字必须事先定义,JavaScript是松散类型语言,并没有遵守这个规则,JavaScript程序里面可以定义任意数量的属性,我们去访问的时候是通过标识符来表示,他们不是一种数据类型,因此不能被程序操作

在通过方括号([ ])这种数组表示法访问对象属性时,属性名是通过字符串来表示

继承

JavaScript对象有一组“自有属性”,同时也从他们的原型对象继承一组属性

属性访问错误

属性访问表达式并不总是会返回设置值
查询不存在的属性不是错误,如果o的自有属性和继承属性中都有没找属性x,则属性访问表达式o.x的求值结果为undefined

let m={}
m.x   // undefined属性不存在
m.x.length  //TypeError :undefined没有length
解决方法:条件式属性访问
m?.x?.length  //undefined

删除属性

delete操作符 用于从对象中移除属性

要注意的地方

delete只能删除自生属性不能删除继承属性(要删除继承属性,就必须从定义属性的原型对象上删除,但是这样做会影响到继承改原型的所有对象)

还有一个地方需要注意

使用delete的时候它存在删除会返回true 但是再吃一次执行同样是true 还有一个地方要注意的是对非属性表达式使用delete 同样也是返回true

 let x={m:2}
    delete x.m  //true
    delete x.m  //true
    delete x.toString //true
    delete i  //true

测试属性

JavaScript对象可以被想象成一组属性,实际开发中经常需要测试这组属性的成员关系,即检查对象是否有一个给定名字的属性,为此,可以使用in操作符,或者hasOwnProperty( ) , propertyIsEnumerable( )方法,或者直接查询相应属性。

in操作符要求左边是一个属性名,右边是一个对象,

let o={x:1}
"x" in o //true
"y" in o //false
"toString" in o  //true  因为继承tostring属性

对象的hasOwnProperty()方法用于测试对象是否有给定名字的属性,对继承的属性,他返回false

let o={x:1}
o.hasOwnProperty("x") //true
o.hasOwnProperty("y") //false
o.hasOwnProperty("toString")  //false

propertyIsEnumerable()方法细化了hasOwnProperty()测试

let o={x:1}
o.propertyleEnumerable("x")     //true
o.propertyleEnumerable("y")    //false
object.prototype.propertyleEnumerable("toString") //flase:toString 不可枚举

除了使用in操作符,通过简单的属性查询配合!==

let o={x:undefined}
o.x!==undefined  //false 属性x的属性存在但是值是undefined
o.y!==undefined  //false 属性y不存在
"x" in o         //true 属性存在
"y" in o         //false 属性y不存在
"delete" o.x     //删除属性x
"x" in o         //false 属性x不存在了

枚举属性

除了属性是否存在,有时候也需要遍历或获取对象的所有属性,为此有几种不同的实现方式

for/in循环对指定对象的每个可枚举(自有或继承)属性都会运行一次循环体,将属性的名字赋值循环变量。对象继承的内置方法是不可枚举,但你的代码添加给对象的属性默认是可枚举的

let o={x:1,y:2,z:3} //3个可枚举自有属性
o.propertyIsEnumerable("toString")  //false :toString不可枚举
for(let p in o){ //遍历循环属性
    console.log(p) //打印x,y,z,但没有toString
}

为防止通过for/in 枚举继承的属性,可以在循环体内部添加一个显示测试

for(let p in o){
	if(!o.hasOwnProperty(p)) contiue; //跳过继承属性
}
for(let p in o){
	if(typeof o[p]==="function") contiue //跳过所有方法
}

使用for/in循环,有时候可以先获取对象所有属性名的数组,然后再通过for/of循环遍历该数组。有4个函数可以用来取得属性名数组:

  • Object.keys()返回对象可枚举自有属性名的数组。不包含不可枚举属性,继承属性或名字是符号的属性
  • Object.getOwnPropertyNames()与Object.key()类似,但也会返回不可枚举自有属性名的数组,只要它们的名字是字符串
  • Object.getOwnPropertySymbols()返回名字是符号的自有属性,无论是否可枚举
  • Reflect.ownKeys()返回所有属性名,包括可枚举和不可枚举属性,以及字符串属性和符号属性

属性枚举顺序

扩展对象

在JavaScript程序中,把一个对象的属性复制到另一个对象上很常见的

let target={x:1},source={y:2,z:3}
for(let key of Object.keys(source)){
	target[key]=source[key]
}
target

但因为这是一个

序列化对象

对象序列化是把对象的状态转化为字符串的过程,之后可以从中恢服对象的状态,函数JSON.stringify()和JSON.parse()用于序列化和恢复JavaScript对象。这二个函数使用JSON数据交互格式。其语法与JavaScript对象和数组字面量十分相似

let o={x:1,y:{z:[false,null,""]}}; //定义一个测试对象
let s=JSON.stringify(o) //s==`{"x":1,"y":{"z":[false,null,""]}}`
let p=JSON.parse(s) //p=={x:1,y:{z:[false,null,""]}}

JSON语法是JavaScript语法子集

对象方法

所有JavaScript对象(除了哪些显式创建为没有原型的)都从Object.prototype继承属性。这些继承的属性主要是方法,因为它们几乎无处不在,所以的JavaScript程序而言特别重要

toString()方法

toString()方法不接收参数,返回表示调用它的对象的值的字符串。每当需要把一个对象转换为字符串,JavaScript就会调用该对象的这个方法

默认的toString()方法并不能提供太多信息

let s={x:1,y:1}.toString();//s=="[object object]"

let point ={
	x:1,
	y:2,
	toString:function(){return `(${this.x},${this.y})`;}
}
String(point)  //”(1,2)“:toSring()用于转换为字符串

toLocaleString()

对象也都有一个toLocaleString()方法。这个方法的用途是返回对象的本地化字符串表示。object定义的默认toLocaleString()方法本身没有实现任何本地化,而是简单地调用toString()并返回该值

let point={
	x:1000,
	y:2000,
	toString:function(){retrun `(${this.x},${this.y})`},
	toLocaleString:function{
		retrun `(${this.x.toLocaleString()},${this.y.toLocaleString()})`
	};
	point.toString() //"(1000,2000)"
	point.toLocaleString() //"(1,000,2,000)"
}

valueOf()方法

valueOf()方法与toString()方法很相似,但会在Javascript需要把对象转换为某些非字符串原始值(通常是数值)时被调用。如果在需要原始值的上下问中使用对象,JavaScript会自动调用这个对象的valueOf()方法

let point={
x:3,
y:4,
valueOf:function(){return Math.hypot(this.x,this.y)}
}
Number(point)  //5:valueOf()
point>4  //
point>5  //false
point<6  //true

toJSON()方法

object.prototype实际上并未定义toJSON()方法,但JSON.stringify()方法会从要序列化的对象上寻找toJSON()方法,如果要序列化的对象上存在这个方法,就会调用它,然后序列化该方法的返回值,而不是原始对象。Date类定义了自己的toJSON方法,返回一个表示日期的序列化字符串

let point ={
	x:1,
	y:2,
	toString:function(){retrun `(${this.x},${this.y})`},
    toJSON:function(){retrun this.toString()}
}

对象字面量扩展语法

简写属性

假设x和y中保存着值,而你想创建一个具有属性x和y且值分别为相应变量值的对象。如果使用基本的对象字面量语法,需要把每个标识符重复二次

let x=1,y=2
let o={
    x:x,
    y:y
};
简写
let x=1,y=2
let o={x,y}

计算的属性名

我们需要创建一个具有特定属性的对象,但该属性的名字不是编译时可以直接写在源代码中的常量。相反,你需要的这个属性名保存在一个变量里,或者是调用的每个函数的返回值。不能对这种属性使用基本对象字面量。为此,必须先创建一个对象,然后再为它添加想要的属性

const pr_name="p1"
function comName(){return "p"+2}
let o={}
o[pr_name]=1
o[comName()]=2

简写
const pr_name="p1"
function comName(){return "p"+2}
let o={
    [pr_name]:1,
    [comName()]:2
}
o.p1+o.p2

符号作为属性名

计算属性语法也让另一个非常重要的对象字面量特性成为可能。属性名可以是字符串或符号。如果把符号赋值给一个变量或常量,那么可以使用计算属性语法将该符号作为属性名

const exsion=Symbol("my exsion symbol")
let o={
	[exsion]:
}
o[exsion].x=0

扩张操作符

可以在对象字面量中使用”扩展操作符" . . . 把己有对象的属性复制到新对象中

let position={x:0,y:0}
let dimensions={width:100,height:75}
let rect={...position, ...dimensions}
rect.x+rect.y+rect.width+rect.height

如果扩展对象和被扩展对象有同一个属性,那么这个属性的值由后面的对象决定

let o={x:1}
let p={x:0,...o}
p.x  //1 覆盖了初始值
let q={...o,x:2}
q.x  //2 覆盖了前面对象o的值

如果扩张对象和被扩展对象有同名属性,这个属性的值由后面的对象决定

注意

扩展操作符只扩展对象的自有属性,不扩展任何继承属性

let o=Object.create({x:1}) //o继承属性x
let p={...o}
p.x  //undefined

简写方法

在把函数定义为对象的属性时,我们称函数为方法,在之前我们是这样定义的

let square={
	area:function(){retrun this.side*this.side},
	side:10
}
square.area()  //100

对象字面量语法经过扩展,允许一种省略function关键字和冒号的简写方法

let square={
	area(){retrun this.side*this.side},
	side:10
}
square.area() //100

属性名可以是对象字面量允许的任何形式,也可以使用字符串字面量和计算的属性名,包括符号属性名

const me_name="m"
const symbol=Symbol()
let wthods={
	"method with spaces"(x) {retrun x+1},
	[me_name](x){retrun x+2},
	[symbol](x) {return x+3}
	
}
wthods["method with spaces"](1)   //2
wthods[me_name](1)    //3
wthods[symbol](1)     //4

属性的获取方法与设置方法

对象属性都是数据属性,即有一个名字和一个普通的值。除了数据属性之外,JavaScript还支持为对象定义访问器属性,这种属性不是一个值,而是一个或二个访问器方法:一个获取方法(getter)和一个设置方法(setter)

如果一个属性既有获取方法也要设置方法,则该属性是一个可读写属性。如果只有一个获取方法,那它就是只读属性。如果只有一个设置方法,那它就是只写属性,读取这种属性始终会得到undefined

let o={
	//一个普通的数据属性
	dataProp:values,
	//通过一对函数定义的一个访问器属性
	get accessorProp(){return this.dataProp},
	set accessorProp(value){this.dataProp=value}
}



let p={
	x:1.0,
	y:1.0,
	get r(){return Math.hypot(this.x,this.y)},
	set r(newvalue){
		let oldvalue = Math.hypot(this.x,this.y);
		let ratio=newvalue/oldvalue;
		this.x*=ratio;
		this.y*=b;
	},
	get theta(){return Math.atan2(this.y,this.x)}
};
p.r
p.theta   

与数据属性一样,访问器属性也是可以继承的。因此,可以把上面定义的对象p作为他点的原型,可以给新对象定义自己的x和y属性,而它们将继承r和theta属性

let q=Object.create(p)
q.x=3;q.y=4
q.r
q.theta
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值