在这篇博客文章中,我们首先来看看由Robin Ricard和Rick Button提出的ECMAScript提案“Record&;Tuple”。该方案在JavaScript中增加了两种复合原始值:
目前,JavaScript仅按值比较诸如字符串之类的原始值(通过查看其内容):
相反,对象按身份进行比较(对象仅与其自身严格相等):
>;{x:1,y:4}=={x:1,y:4}false>;[';a';,';b';]=[';a';,';b';]false。
Proposal Record&;Tuple(由Robin Ricard和Rick Button编写)允许我们创建按值进行比较的复合值。
例如,通过在对象文本前面加上数字符号(#),我们创建了一条记录-一个按值比较且不可变的复合值:
如果我们以#作为Array文字的前缀,则会创建一个tuple-一个按值比较且不可变的Array:
按值比较的复合值称为复合基元值或复合基元。
警告:这些转换很浅。如果值树中的任何节点不是原始节点,那么Record()和Tuple.from()将抛出异常。
const record=#{x:1,y:4};//访问属性assert.equal(record.y,4);//Destructuringconst{x}=Records;assert.equal(x,1);//Spreadingassert.ok(#{.record,x:3,z:9}=#{x:3,y:4,z:9});
const tuple=#[';a';,';b';];//访问elementsassert.etc(tuple[1],';b';);//解构(元组是可迭代的)const[a]=tuple;assert.etc(a,';a';);//Spreadingassert.ok(#[.tuple,';c';]=。,';c';]);//Updatingassert.ok(tuple.with(0,';x';)=#[';x';,';b';]);
某些数据结构(如散列映射和搜索树)具有根据其值放置关键字的槽。如果密钥的值发生更改,则通常必须将其放入不同的插槽中。这就是为什么在JavaScript中,可以用作键的值是:
深入比较对象-这是一个内置操作,可以调用,例如,VIA=。
共享值:如果一个对象是可变的,如果我们想要安全地共享它,我们需要深入复制它。对于不变的值,共享不是问题。
数据的非破坏性更新:当我们创建复合值的修改副本时,我们可以安全地重用该复合值的某些部分(因为所有内容都是不可变的)。
使用诸如Maps和Set这样的数据结构:它们变得更加强大,因为在语言中的任何地方(包括Maps的键和Set的元素),具有相同内容的两个复合原语被认为是严格相等的。
使用复合原语,即使它们是复合的(而不是原子的,就像原始值一样),我们也可以消除重复项:
由于对象是按标识进行比较的,因此将它们用作(非弱)映射中的键几乎没有意义:
如果我们使用复合原语,这就不同了:A行中的Map将地址(记录)映射到名称。
Const People=[#{name:';Eddie';,address:#{Street:';1313 Mockingbird Lane';,City:';Mockingbird Heights';,},},#{name:';Dawn';,地址:#{Street:';1630 Revello Drive';,City:';Sunnydale';,},},,地址:#{Street:';1313 Mockingbird Lane';,City:';,},},#{name:';Joyce';,地址:#{Street:';1630 Revello Drive';,City:';Sunnydale';,},},];const address sToNames=new Map();//(A)for(Const Person Of Person){if(!AddressToNames.has(Person.address)){AddressToNames.set(Pers.address,new set());}addressToNames.get(person.address).add(person.name);}assert.deepEqual(//将映射转换为键值对数组,//这样我们就可以通过assert.deepequal.()进行比较。[.AddressToNames],[#{Street:';1313 Mockingbird Lane';,City:';Mockingbird Heights';,},new set([';Eddie';,';Herman';]),],[#{Street:';1630 Revello Drive';,City:';Sunnydale';,},New。Joyce';])),],]);
在下面的示例中,我们使用Array方法.filter()(行B)提取地址等于地址(行A)的所有条目。
Const People=[#{name:';Eddie';,address:#{Street:';1313 Mockingbird Lane';,City:';Mockingbird Heights';,},},#{name:';Dawn';,地址:#{Street:';1630 Revello Drive';,City:';Sunnydale';,},},,地址:#{Street:';1313 Mockingbird Lane';,City:';Mockingbird Heights';,},},#{name:';Joyce';,地址:#{Street:';1630 Revello Drive';,City:';Sunnydale';,},},];Const Address=#{//(A)Street:',城市:';Sunnydale';,};assert.Deep Equity(Persons.filter(p=>;p.address=地址),//(B)[#{name:';Dawn';,address:#{Street:';1630 Revello Drive';,City:';Sunnydale';,},},#{name:';Joyce';,地址:#{Street:';1630 Revello Drive';,City:';Sunnydale';,},},#{name:';Joyce';,地址:#{Street:'。,城市:';Sunnydale';,},},]);
每当我们处理缓存数据(如下例中的previousData)时,内置的深度相等功能使我们可以有效地检查是否有任何更改。
让previousData;function Displaydata(Data){if(data=previousData)返回;//··}Displaydata(#[';Hello';,';world';]);//displayeddisplayData(#[';Hello';,';world';]);//不显示
大多数测试框架都支持深度相等,以检查计算是否产生预期结果。例如,内置的Node.js模块assert具有函数DeepEquity()。对于复合原语,我们有一种替代此类功能的方法:
函数invert(Color){return#{red:255-color.red,green:255-color.green,};}assert.ok(invert(#{red:255,green:153,Blue:51})=#{red:0,green:102,Blue:204});
注意:考虑到内置的相等检查不只是比较值,它们更有可能支持复合原语并且对它们更有效(与过时的检查相比)。
新语法的一个缺点是,字符#已经在其他地方使用(用于私有字段),并且非字母数字字符总是稍显隐晦。我们可以在这里看到这一点:
Const della=#{name:';Della';,孩子:#[#{name:';Huey&39;,},#{name:';Dewey&39;,},#{name:';Louie&39;,},],};
优点是该语法非常简洁。如果经常使用某个构造,并且我们希望避免冗长,那么这一点很重要。此外,因为我们已经习惯了这种语法,所以隐蔽性就不是什么问题了。
const della=记录({:';Della';,:tuple([ord({:';Huey&39;,}),Record({:';Dewey&39;,}),Record({:';Louie&39;,}),],});
如果JavaScript支持标记集合文字(Kat Marchán提出的建议,但她已撤回),则可以改进此语法:
const della=记录!{:';Della&39;,:tuple![RECORD!{:';Huey&39;,},Record!{:';Dewey&39;,},Record!{:';Louie';,},],};
const R=记录;const T=Tuple;const della=R!{:';Della';,:t![r!{:';Huey&39;,},R!{:';Dewey&39;,},R!{:';Louie';,},],};
parseImmutable的工作原理与JSON.parse()类似,但是返回记录而不是对象和元组,而不是数组(递归)。
我喜欢使用通常只是数据容器的类,而不是普通的对象或数组,因为它们将名称附加到对象。出于这个原因,我希望我们最终能得到实例不变并按值进行比较的类。
如果我们还支持深度、非破坏性地更新包含值类型类生成的对象的数据,那就太好了。
在其他人中,以下人回复了我的一条推文,并对这篇博客帖子做出了贡献:@asp_net,@blomret,@imchrisitchens,@jamiedixon,@mattxcurtis,@orangecms。