当前位置: 首页 > news >正文

对象的扩展

9.1属性的简洁表示法

ES6允许直接写入变量和函数作为对象的属性和方法。这样的书写更加简洁。
	var foo = 'bar';var baz = {foo};baz //{ foo: "bar"}//等同于var baz = {foo:foo};上面的代码表明,ES6允许在对象中只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。下面是另一个例子,function f(x,y) {return {x,y};}//等同于funciton f(x,y) {return {x:x,y:y};}f(1,2) //Object {x: 1,y: 2}除了属性简写,方法也可以简写。var o = {method() {return "Hello!";}};//等同于var o = {method:function() {return "Hello!";}}这种写法用于函数的返回值会非常方便。function getPoint() {var x = 1;var y = 10;return {x,y};}getPoint()// {x:1,y:10};

9.2 属性名表达式

JavaScript语言定义对象的属性有两种方法。
	//方法一obj.foo = true;//方法二obj['a'+'bc'] = 123;上面的方法一是直接用标识符作为属性名;方法二是用表达式作为属性名,这时要将表达式放在方括号内,但是,如果使用字面量方式定义对象(使用大括号),则在ES5中只能使用方法yi(标识符)定义属性var obj = {foo:true;abc:123};ES6允许字面量定义对象时用方法二(表达式作为对象的属性名),即把表达式放在方括号内。let propKey = 'foo'llet obj = {[propKey]:true,['a'+'bc']:123};下面是另一个例子。var lastWord = 'last word';var a = {'first word':'hello',[lastWord]:'world'};a['first word'] //"hello"a[lastWord ] //"world"a['last word'] //"world"表达式还可以用于定义方法名。let obj = {['h' + 'ello']() {return 'hi';}};obj.hello() //hi

9.3 方法的name属性

函数的name属性返回函数名。对象方法也是函数,因此也有name属性。
	const person = {sayName() {console.log('hello!');}};person.sayName.name //"sayName"上面的代码中,方法的name属性返回函数名(即方法名)。如果对象的方法使用了取值函数

如果对象的方法使用了取值函数(getter)和存值函数(setter)则name 属性不实在该方法上面,而是在该方法属性的描述对象的get和set属性上面,返回值是方法名加上get和get。

有两种特殊情况:bind方法创造的函数,name属性返回“bound”加上原函数的名字,Function构造函数创造的函数,name属性返回anonymous

9.4 Object.is()

ES5比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符( ===)。他们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0.JavaScript缺乏这样一种运算:在所有环境中,只要两个值是一样的,他们就应该相等。
ES6提出 “Same-value equality”(同值相等)算法用来解决这个问题。Object.is就是部署这个算法的新方案。它用来比较两个值是否严格相等,与严格相等运算符( === )的行为基本一致。
不同之处只有两个:一是+0 不等于-0 二十NaN等于自身。

	Object.is(+0,-0) //falseObject.is(NaN.NaN) //true

9.5 Object.assign()

9.5.1 基本用法
Object.assign 方法用于将源对象(source)的所有可枚举属性复制到目标对象(target)。

	var target = { a:1 };var source1 = { b:2 };var source2 = { c:3 };Object.assign(target,source1,source2);target // {a:1,b:2,c:3}
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象
注意! 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
	var target = { a:1,b:1 };var source1 = { b:2,c:2 };var source2 = { c:3 };Object.assign(target,source1,source2);target // {a:1,b:2,c:3}如果只有一个参数Object.assign会直接返回该参数。var obj = { a:1 };Object.assign(obj) === obj //true;如果该参数不是对象,则会先转成对象,然后返回由于undefinednull无法转为对象,所以如果将他们作为参数,就会报错。Object.assign(undefined)  //报错;Object.assign(null) //报错;如果非对象参数出现在源对象位置,那么处理规则将有所不同,首先这些参数都会转成对象,如果无法转成对象便会跳过。这意味着。如果undefinednull不在首参数变不会报错。var obj= { a:1 };Object.assign(obj,undefined) === obj //true;Object.assign(obj,null) === obj //true;其他类型的值(即数值,字符串和布尔值)不再首参数也不会报错。但是,除了字符串会以数组的形式复制到目标对象,其他值都不会产生效果。

Object.assign复制的属性是有限制的,只复制源对象的自身属性(不复制继承属性)。也不复制不可枚举的属性

9.5.2 注意点

Object.assign方法实行的浅复制,而不是深复制。也就是说,如果源对象某个属性的值是对象,那么目标对象复制得到的是这个对象的引用。

	var obj1 = {a: {b:1}};var obj2 = Object.assign({},obj1);obj1.a.b = 2;obj2.a.b //2
上面的代码中,源对象obj1的a属性的值是一个对象,Object.assign复制得到的是这个对象的引用。这个对象的任何变化都会反映到目标对象上面。
对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换而不是添加。var target = { a: { b:'c',d: 'e'} }var source = {a : {b: 'hello' }}Object.assign(target,source)//{ a: {b: 'hello' } }
上面的代码中,target对象的a属性被source对象的a属性整个替换掉了,而不会得到{ a:{ b:'hello', d:'e' } }的结果。这通常不是开发者想要的,需要特别小心。
有一些函数库提供Object.assign的定制版本,可以解决浅复制的问题,得到深复制的合并。
注意,Object.assign可以用来处理数组,但是会把数组视为对象来处理Object.assign([1,2,3],[4,5])// [4,5,3]上面的代码中,Object.assign把数组视为属性名为012的对象,因此目标数组的0号属性4覆盖了原数组的0号属性1

9.5.3 常见用途

Object.assign方法有很多用处。
为对象添加属性
为对象添加方法

Object.assign(SomeClass.prototype,{someMethod(arg1,arg2){。。。}anotherMethod() {...}
});

克隆对象

function clone(origin){return Object.assign({},origin);}
上面的代码将原始对象复制到一个空对象中,就得到了原始对象的克隆。
不过,采用这种方法只能克隆原始对象自身的值,不能克隆他继承的值。如果想要保持继承链,可以采用下面的代码function clone(origin) {let originPrptp = Object.getPrototypeOf(orighin);return Object.assign(Object.create(originProto),origin);}

合并对象
将多个对象合并到某个对象。

	const merge = (target,...sources) => Object.assign(target,...sources);如果希望合并后返回一个新对象,可以改写上面的函数,对一个空对象合并。const merge = (...sources) => Object.assign({},...sources);

为属性指定默认值

9.6 属性的可枚举性

对象的每一个属性都具有一个描述对象,用于控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
	let obj = {foo: 123};Object.getOwnPropertyDescriptor(obj,'foo')//{//	value :123,//	writable:true,//  enumerable:true,//	configurable:true,//}描述对象的enumerable属性称为可枚举性,如果该属性为false,就表示某些操作会忽略当前属性

ES5有3哥操作会忽略enumerable为false的属性。

  • for…in循环,只便利对象自身的和继承的可枚举属性,
  • Object.keys();只返回对象自身的所有可枚举属性。
  • JSON.stringify(); 只串行化对象自身可枚举属性。
    ES6新增1个操作Object.assign(),会忽略enumerable为false的属性,只复制对象自身的可枚举属性。
    这4个操作之中,只有for … in 会返回继承的属性。实际上,引入enumerable的最初目的就是让某些属性可以规避掉for…in操作,比如,对象原型的toString方法以及数组的length属性,就通过这种手段而不被for…in遍历到。
	Object.getOwnPropertyDescriptor(Object.prototype,'toString').enumerable// falseObject.getOwnPropertyDescriptor([],'length').enumerable// false上面的代码中,toString和length属性的enumerable都是false,因此for...in不会遍历到这两个继承自原型的属性。另外,ES6规定,所有的class的原型的方法都是不可枚举的。

9.7 属性的遍历

ES6一共有5种方法可以遍历对象的属性。

  1. for…in
    for…in循环遍历对象自身的和继承的可枚举属性

  2. Object,keys(obj)
    Object,keys返回一个数组,不包括对象自身的(不含继承的)所有可枚举属性

  3. Object.getOwnOrioertyNames(obj)
    Object.getOwnOrioertyNames返回一个属性,包含对象自身的所有属性

  4. Object.getOwnPropertySymbol(obj)
    Object.getOwnPropertySymbol返回一个数组,包含对象自身所有的Symbol属性。

  5. Reflect.ownKeys(obj)
    Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管属性名是Symbol还是字符串,也不管对象是否可枚举。

9.8 Object.keys(),Object.values().Object.entries()

9.8.1 Object.keys()
ES5引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不包含继承的)所有可遍历属性的键名。

	var obj = { foo:'bar',baz:42 };Object.keys(obj)//{"foo","bza"}
ES2017中有一个提案,其中引入了与Object.keys配套的Object.values和Object.entries作为遍历一个对象的补充手段,供for...of循环使用。let {keys,values,entries} = Object;let obj = { a:1,b:2,c:3 };for (let key of keys(obj)){console.log(key);// 'a','b','c'}for(let value of values(obj)) {console.log(values);// 1,2,3}for(let (key , value) of entries(obj)) {console.log(values);// ['a':1],['b':2],['c':3],}

9.8.2 Object.values()

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历顺序ing的键值。

	var obj = {foo:'bar',baz:42};Object.values(obj)//["bar",42]var obj = [100:'a',2:'b',7:'c'};Object.values(obj)//["b","c","a"]
***属性名为数值的属性,是按照数值大小从小到大遍历的,因此返回的顺序是b,c,a***Object.values()只返回对象自身的可遍历属性。var obj = Object.create({},{p:{value:42}});Object.values(obj) //[]上面的代码中,Object.create方法的第二个参数添加的对象属性(p)如果不显示声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是false,Object.values不会返回这个属性,只要把enumerable改为true,Object.values就会返回属性P的值var obj = Object.create({},{p:{value:42,enumerable:true}
});
Object.values(obj) [42]
Object.values会过滤属性名为Symbol值的属性
Object.values({ [Symbol()];123,foo:'abc'});
//['abc']
如果Object.values方法的参数是一个字符串,则会返回各个字符传组成的一个数组.
Object.values('foo')
//['f','o','o']
上面的代码中,字符串会先转成一个类是数组的对象,字符串的每个字符就是该对象的一个属性,因此,Object.values返回每个属性的键值就是各个字符组成的数组.如果参数不是对象Object.values会先将其转为对象.由于数值和布尔值的包装对象都不会为实例添加非继承的属性,所以Object.values会返回空数组.Object.values(42)//[]Object.values(true)//[]

9.8.3 Object.entries

Object.entries方法返回一个数组,成员是参数对象自身(不包含继承)所有可遍历的(enumerable)属性的键值对数组。

	var obj = { foo:'bar',baz:42 };Object.entries(obj)// [ [ "foo","bar"],{"baz",42] ]除了返回值不一样,该方法的行为与Object.value基本一致。如果原对象的属性名是一个Symbol值,该属性会被忽略。

9.12 Null传导运算符

编程实务中,如果读取对象内部的某个属性,往往需要判断该对象是否存在,比如要读取message.body.user.firstName,安全的写法如下。

	const firstName = (message&& message.body&& message.body.user&& message.body.user.firstName) || 'default';
这样层层判断非常麻烦,因此现在有一个提案,const firstName = mewssage?.body?.user?.firstName || 'default';
上面的代码有3?.运算符,只要其中一个返回nullundefined,就不再继续运算,而是返回undefined

“Null传导运算符”有4种用法

  • obj?.prop;读取对象属性
  • obj?.[expr];同上
  • func?.(…args);函数或对象方法的调用
  • new C?.(…args)
    传导运算符之所以写成obj?.prop而不是obj?prop,是为了方便编译器能够区分三元运算符?:
//如果a是null或undefined返回undefined
//否则返回a.b.c().da?.b.c().d//如果a是null或undefined,下面的语句不产生任何效果
//否则执行a.b = 42a?.b = 42
//如果a是null或undefined,下面的语句不产生任何效果
delete	a?.b 

相关文章:

  • Golang 并发编程(Goroutine、Channels、Select、Sync、原子操作函数、Context、gpool)
  • 深入探索面向对象编程(OOP):封装、继承和多态的实际应用
  • Android找不到so,实际上apk中有的
  • jQuery中.text() 和 .val()辨析
  • 从“野人饭”走红,探索品牌户外化营销趋势丨小红书内容分析
  • 【js判断机型】
  • ubuntu22.04禁止自动休眠的几种方式
  • JUnit 5学习笔记
  • Android 断点续传进阶之多线程下载
  • 资源宝库网站!人人必备的神器!
  • 基于二进制构建Kubernetes 1.28 高可用集群
  • cs144 LAB1 基于滑动窗口的碎片字节流重组器
  • 解决动态权限路由页面刷新空白404
  • 你不知道的MySQL备份和还原技巧,速来学习!
  • Java17 --- SpringSecurity之前后端分离处理
  • Bytom交易说明(账户管理模式)
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • GitUp, 你不可错过的秀外慧中的git工具
  • Hexo+码云+git快速搭建免费的静态Blog
  • Sass Day-01
  • uni-app项目数字滚动
  • webgl (原生)基础入门指南【一】
  • 前端性能优化--懒加载和预加载
  • 三栏布局总结
  • 使用API自动生成工具优化前端工作流
  • 数据仓库的几种建模方法
  • 我的面试准备过程--容器(更新中)
  • 小程序 setData 学问多
  • 智能合约开发环境搭建及Hello World合约
  • 大数据全解:定义、价值及挑战
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • ()、[]、{}、(())、[[]]命令替换
  • (2020)Java后端开发----(面试题和笔试题)
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (一) springboot详细介绍
  • (转)为C# Windows服务添加安装程序
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .net core Swagger 过滤部分Api
  • .NET Framework与.NET Framework SDK有什么不同?
  • .net SqlSugarHelper
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • @Transactional 竟也能解决分布式事务?
  • [001-03-007].第07节:Redis中的管道
  • [2024-06]-[大模型]-[Ollama]- WebUI
  • [C# 基础知识系列]专题十六:Linq介绍
  • [C#基础知识系列]专题十七:深入理解动态类型
  • [CC-FNCS]Chef and Churu