beanutils.copyproperties属性值丢失_JavaScript中对象剩余/扩展属性的简易指南

ba52b51b84ca231bf4237431e4391070.png

合并多个JavaScript对象是一项常见的任务。不幸的是,JavaScript在提供方便的语法来进行合并方面草率。至少到现在为止。

在ES5中,您的解决方案_.extend(target, [sources])来自Lodash(或任何替代产品),并且ES2015引入Object.assign(target, [sources])

幸运的是,对象传播语法(第3阶段的ECMAScript提议)是如何操作对象的一步,提供了一种简短易懂的语法。

f85b1c726f069d8c0e26305fc978c00d.png

在上面的示例中,...cat将属性复制cat到新对象中dog.sound属性接收最终值'woof'

本文介绍了对象传播和其余语法。包括对象散布如何实现诸如对象克隆合并,属性覆盖等的配方。

接下来是对可枚举属性的简短概述,以及如何区分自己的属性与继承的属性。这些是了解对象散布和休息如何工作的必要基础。

1.可数和自己的属性

JavaScript中的对象是键和值之间的关联。

密钥类型通常是字符串或符号。该值可以是原始类型(字符串,布尔值,数字undefinednull),对象或函数。

以下示例使用对象文字(也称为对象初始化程序)创建对象:

21f78fa1c8ac2302dd3d5988cfd07e56.png

person 对象描述一个人的名字和姓氏。

1.1可枚举的属性

属性具有描述值的几种属性,以及可写,可枚举和可配置的状态。有关更多详细信息,请参见JavaScript中的对象属性。

Enumerable属性是一个布尔值,指示当枚举对象的属性时该属性是否可访问。

您可以使用Object.keys()(访问自己的和可枚举的属性),for..in语句(访问所有可枚举的属性)等枚举对象属性。

在对象文字{ prop1: 'val1', prop2: 'val2' }中明确声明的属性是可枚举的。让我们看看可枚举的属性person对象包含什么:

62dc6190228cfb422107dd7a8d65e595.png

.name.surname是枚举的属性person的对象。

有趣的部分到了。来自源可枚举属性的对象散布副本:

17de946f6fde582c614bfd4188be3580.png

现在,让我们.ageperson对象上创建一个不可枚举的属性。然后查看点差的行为:

942e40ebd50c5c1253a8a1839a493c64.png

.name.surname可枚举的属性从源对象复制personclone。但是不可枚举.age被忽略。

1.2自己的财产

JavaScript包含原型继承。因此,对象属性可以是自己的继承的

在对象常量中显式声明的属性是own。但是,对象从其原型接收的属性是继承的

让我们创建一个对象personB并将其原型设置为person

58c2ec7c4039aa9203784db6bf7f5d7c.png

personB对象具有自己的属性.profession,并从其原型继承.name.surname属性person

对象从源自己的属性传播副本,忽略继承的属性:

63cec9b351184e897bf41b0483ec4cd6.png

对象...personB从源对象传播的副本personB 仅具有.profession自己的属性。继承的.name.surname被忽略。

对象分发语法从源对象复制 自己的和可枚举的属性。与Object.keys()函数返回的相同。

2.对象传播属性

对象文字中的对象传播语法从对象中提取自己的和可枚举的属性,并将其复制到目标对象中。

7f7864adb65050f8e76756bfaf7e2997.png

附带说明一下,在许多方面,对象传播语法与等效Object.assign()。上面的代码也可以这样实现:

65af64e99c627cdb6e793bc00e25f4bb.png

一个对象文字可以具有多个对象散布,可以与常规属性声明任意组合:

2b078be0b357f3114cce1c807aed3095.png

2.1对象传播规则:后一个属性获胜

当散布多个对象并且某些属性具有相同的键时,如何计算最终的最终值集?规则很简单:

后期传播属性将 覆盖具有相同密钥的 早期属性

让我们继续一些例子。以下对象文字实例化了一只猫:

fd0c8deacde4d670d213076f0b4fb1f3.png

让我们玩弗兰肯斯坦博士,把这只猫变成狗。注意.sound财产的价值:

16de519e1ff8278caa4252c7a53549cc.png

后面的值将'woof'覆盖前面的值'meow'(来自cat源对象)。这与后一个属性用相同的密钥覆盖最早的属性的规则匹配。

相同的规则适用于对象初始化程序的常规属性:

fd4dfdf775c3aa99103c4f55b69511fb.png

常规属性sound: 'woof'获胜,因为它是最新的属性。

现在,如果交换散布对象的相对位置,结果将有所不同:

374df5ee679227320b5a781141d0b35d.png

猫仍然是猫。尽管第一个源对象为.sound属性提供了value 'woof',但是它被扩展属性中的后一个'meow'值覆盖cat

对象散布和规则属性的相对位置很重要。扩展语法的这种效果允许执行配方,如对象克隆,合并对象,填充默认值。

让我们详细介绍这些食谱。

2.2克隆对象

使用传播语法锥化对象简短而富有表现力。以下示例创建bird对象的副本:

f9ad347961089033aa0cdc4d4441b321.png

...bird在文字内部复制bird到目标的自身和可枚举的属性birdClone。结果birdClone是的克隆bird

乍一看,克隆似乎很简单,但是有一些细微差别需要注意。

浅拷贝

对象传播对对象进行浅表复制。仅克隆对象本身,而不克隆嵌套实例。

laptop有一个嵌套对象laptop.screen。让我们克隆一下laptop,看看它如何影响嵌套对象:

d76c5c1db5dce7fcf72c321e978bbe95.png

第一个比较laptop === laptopClonefalse。主要对象已正确克隆。

但是laptop.screen === laptopClone.screen评估为true。这意味着laptop.screenlaptopClone.screen引用了未复制的同一嵌套对象。

好消息是您可以在任何级别传播属性。不费吹灰之力也可以克隆嵌套对象:

3e4c678370f92b8f59e57fd13091c24e.png

额外的扩展...laptop.screen确保了嵌套对象也被克隆。很好,现在laptopDeepClonelaptop对象的完整克隆。

原型丢失

下面的代码片段声明一个类Game,并创建该类的实例doom

d02ca42cad2f666cc1fda8ecee7e6987.png

现在,让我们克隆doom从构造函数调用创建的实例。这可能会导致意外:

cbfd241559f0ddbeae173c0b79e07b57.png

...doom将自己的财产复制.name到中doomClone。仅此而已。

doomClone是一个普通的JavaScript对象,原型为Object.prototype,但并非Game.prototype预期的那样。对象传播不会保留源对象的原型。

因此调用doomClone.getMessage()会抛出一个TypeError,因为doomClone它不会继承getMessage()方法。

要修复丢失的原型,请使用手动指示__proto__

ef54b3e71fe7bcdffe5876c17f84880e.png

__proto__对象文字内部的内容确保doomFullClone了必要的原型Game.prototype

不要在家尝试__proto__已弃用。我将其仅用于演示。

对象传播滞后于通过构造函数调用创建的实例,因为它不保留原型。目的是以浅薄的方式传播自己的和可枚举的属性,因此忽略原型的方法似乎是合理的。

附带说明一下,还有一种更合理的克隆doom方法Object.assign()

5b9480fbb6a88a488d4ca87230dcdde6.png

好的,有了原型就足够了。我承诺。

2.3不可变对象更新

当同一对象在应用程序的许多地方共享时,直接对其进行修改可能会导致意外的副作用。跟踪此类修改是一项繁琐的任务。

更好的方法是使操作不变。不变性可以更好地控制对象的修改,并倾向于编写纯函数。即使在复杂的场景中,也因为数据流向单个方向,所以更容易确定对象更新的来源和原因。

对象散布便于以不变的方式修改对象。假设您有一个描述书籍版本的对象:

883e2d9c830f3c6bbfa82b50a9b0bb3c.png

然后新的第六版问世。对象传播让您以不变的方式对此方案进行编程:

165bdccca77fe864f6e27a8ab39fdedd.png

...book字面量内的book对象传播属性。手动枚举的属性edition: 6year: 2011设置更新的属性值。

在末尾指定有效属性值,以匹配扩展规则,即后一个属性值使用相同的键覆盖前一个值。

newerBook是具有更新属性的新对象。同时,原件book保持原样。不变性得到满足。

2.4合并对象

合并很简单,因为您可以传播任意数量的对象的属性。

让我们合并3个对象以创建一个复合对象:

aed01b5d7c9a20d1ae597c37b715615b.png

car对象是通过合并三个对象创建的part1part2part3

不要忘记后一个财产胜出规则。它给出了合并具有相同键的多个对象的理由。

让我们更改一下前面的示例。现在part1part3拥有一个新属性.configuration

6e39c907394a9c526e5cbae77e93218d.png

第一个对象传播...part1将的值设置.configuration'sedan'。然而,后一个对象散布将...part3覆盖先前的.configuration值,最终使其变为'hatchback'

2.5使用默认值填充对象

一个对象在运行时可以具有不同的属性集。可能设置了某些属性,而其他属性可能会丢失。

在配置对象的情况下,可能会发生这种情况。用户只能指定配置​​的重要属性,但未指定的属性取自默认值。

让我们实现一个以给定宽度分成多行的multiline(str, config)函数str

config 对象接受以下可选参数:

  • width:要中断的字符数。默认为10;
  • newLine:要添加到行末的字符串。默认为n;
  • indent:要预定行的字符串。默认为空字符串''

multiline()运作方式的几个例子:

78e820a24e10cf3023999ea95e44baef.png

config 参数接受不同的属性集:您可以指定1、2或3个属性,甚至根本不指定任何属性。

使用对象传播非常简单,即可用默认值填充配置对象。在对象文字内部,首先传播默认对象,然后传播配置对象:

49d059519320d05480453df3b3c68f3f.png

让我们探索safeConfig对象文字。

对象传播...defaultConfig从默认值中提取属性。然后...config使用自定义属性值覆盖以前的默认设置。

结果safeConfig具有multiline()主代码可以使用的全套属性。不管输入config会丢失某些属性,您都相信它safeConfig具有必要的值。

对象传播的默认设置实现非常直观。

2.6“我们需要更深入”

关于对象传播的最酷的事情是可以在嵌套对象上使用。在更新大对象时,这是一个很大的可读性胜利,建议在Object.assign()替代方法上胜出。

以下box对象定义了一个项目框:

235533f60724f02b9daac2b394346d7c.png

box.size描述盒子的大小并box.items枚举盒子中包含的项目。

要通过增加来使框变高box.size.height,只需将属性分布在嵌套对象上:

c975e7895e429154dda71d72a05cbe4c.png

...box确保biggerBoxbox源接收属性。

更新嵌套对象的高度box.size需要附加的对象文字{ ...box.size, height: 200 }。此文字将的属性传播box.size到新对象,并将高度更新为200

我喜欢通过一条语句执行多个更新的可能性。

将颜色更改为black,将宽度增加为400并添加新项目ruler(使用spread array)怎么样?这很简单:

09d58a6afd25c8046901a90d96577ee2.png

2.7传播未定义,空值和基元

当扩展的性质undefinednull或一个原始值没有性能被提取,并且没有错误被抛出。结果是一个普通的空对象:

8aed59ad385375e13d4b5054d8b4227e.png

对象散布提取无论从性能nothingmissingObjecttwo

当然,没有理由在原始值上使用对象散布。

3.对象其余属性

使用解构分配将对象的属性提取为变量后,可以将其余属性收集到其余对象中。

这是对象剩余属性很好地表现的:

eee57c57994873b987fb0573e40595d3.png

销毁分配定义了一个新变量width,并将其值设置为style.width。其余对象...margin解构赋值内收集剩余的属性marginLeftmarginRight到对象margin

对象剩余仅收集自己的和可枚举的属性。

请注意,对象其余部分必须是解构分配中的最后一个元素。因此,该代码const { ...margin , width } = style无效,并会触发SyntaxError: Rest element must be last element

4。结论

对象传播要记住一些规则:

  • 它从源对象中提取自己的和可枚举的属性
  • 后期传播属性使用相同的密钥覆盖较早的属性

同时,对象传播简短而富有表现力,可以很好地在嵌套对象上使用,同时保持更新的不变性。它使使用默认属性轻松实现对象的克隆,合并和填充。

通过对象剩余语法实现解构分配后收集其余属性。

实际上,对象休息和传播属性是JavaScript的重要补充。

原著作者:德米特里·帕夫鲁汀

文章来源:国外

原著链接:

Dmitri Pavlutin Blog​dmitripavlutin.com
343d4127ac9d801b21dcedfbbf7aff34.png

PS:原著文章内容为英文版本,建议使用360极速浏览器进行翻译阅读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值