文章插圖

文章插圖
前言
Dr. Axel Rauschmayer最近撰文介紹了還處于Stage1階段的兩個JavaScript新特性:記錄和元組 。
記錄和元組是一個新提案(Record & Tuple , https://github.com/tc39/proposal-record-tuple) , 建議為JavaScript增加兩個復合原始類型:
記錄(Record) , 是不可修改的按值比較的對象元組(Tuple) , 是不可修改的按值比較的數組什么是按值比較
當前 , JavaScript只有在比較原始值(如字符串)時才會按值比較(比較內容):
>'abc'==='abc'true但在比較對象時 , 則是按標識比較(by identity) , 因此對象只與自身嚴格相等:>{x:1,y:4}==={x:1,y:4}false>['a','b']===['a','b']false“記錄和元組”的提案就是為了讓我們可以創建按值比較的復合類型值 。比如 , 在對象字面量前面加一個井號(#) , 就可以創建一個記錄 。而記錄是一個按值比較的復合值 , 且不可修改:
>#{x:1,y:4}===#{x:1,y:4}true如果在數組字面量前面加一個# , 就可以創建一個元組 , 也就是可以按值比較且不可修改的數組:>#['a','b']===#['a','b']true按值比較的復合值就叫復合原始值或者復合原始類型 。記錄和元組是原始類型
使用typeof可以看出來 , 記錄和元組都是原始類型:
>typeof#{x:1,y:4}'record'>typeof#['a','b']'tuple'記錄和元組的內容有限制記錄:鍵必須是字符串值必須是原始值(包括記錄和元組)元組:元素必須是原始值(包括記錄和元組)把對象轉換為記錄和元組>Record({x:1,y:4})#{x:1,y:4}>Tuple.from(['a','b'])#['a','b']注意:這些都是淺層轉換 。如果值樹結構中有任何節點不是原始值 , Record()和Tuple.from()會拋出異常 。使用記錄
constrecord=#{x:1,y:4};// 訪問屬性assert.equal(record.y,4);// 解構const{x}=record;assert.equal(x,1);// 擴展assert.ok(#{...record,x:3,z:9}===#{x:3,y:4,z:9});使用元組consttuple=#['a','b'];// 訪問元素assert.equal(tuple[1],'b');// 解構(元組是可迭代對象)const[a]=tuple;assert.equal(a,'a');// 擴展assert.ok(#[...tuple,'c']===#['a','b','c']);// 更新assert.ok(tuple.with(0,'x')===#['x','b']);為什么按值比較的值不可修改某些數據結構(比如散列映射和搜索樹)有槽位 , 其中鍵的保存位置根據它們的值來確定 。如果鍵的值改變了 , 那這個鍵通常必須放到不同的槽位 。這就是為什么在JavaScript中可以用作鍵的值:
要么按值比較且不可修改(原始值)要么按標識比較且可修改(對象)復合原始值的好處
復合原始值有如下好處 。
深度比較對象 , 這是一個內置操作 , 可以通過如===來調用 。共享值:如果對象是可修改的 , 為了安全共享就需要深度復制它的一個副本 。而對于不可修改的值 , 就可以直接共享 。數據的非破壞性更新:如果要修改復合值 , 由于一切都是不可修改的 , 所以就要創建一個可修改的副本 , 然后就可以放心地重用不必修改的部分 。在Map和Set等數據結構中使用:因為兩個內容相同的復合原始值在這門語言的任何地方(包括作為Map的鍵和作為Set的元素)都被認為嚴格相等 , 所以映射和集合成會變得更有用 。
接下來演示這些好處 。
示例:集合與映射變得更有用通過集合去重
有了復合原始值 , 即使是復合值(不是原始值那樣的原子值)也可以去重:
>[...newSet([#[3,4],#[3,4],#[5,-1],#[5,-1]])][#[3,4],#[5,-1]]如果是數組就辦不到了:>[...newSet([[3,4],[3,4],[5,-1],[5,-1]])][[3,4],[3,4],[5,-1],[5,-1]]映射的復合鍵因為對象是按標識比較的 , 所以在(非弱)映射中用對象作為鍵幾乎沒什么用:
constm=newMap();m.set({x:1,y:4},1);m.set({x:1,y:4},2);assert.equal(m.size,2)如果使用復合原始值就不一樣了:下面行(A)創建的映射會保存地址(記錄)到人名的映射 。constpersons=[#{name:'Eddie',address:#{street:'1313 Mockingbird Lane',city:'Mockingbird Heights',},},#{name:'Dawn',address:#{street:'1630 Revello Drive',city:'Sunnydale',},},#{name:'Herman',address:#{street:'1313 Mockingbird Lane',city:'Mockingbird Heights',},},#{name:'Joyce',address:#{street:'1630 Revello Drive',city:'Sunnydale',},},];constaddressToNames=newMap();// (A)for(constpersonofpersons){if(!addressToNames.has(person.address)){addressToNames.set(person.address,newSet());}addressToNames.get(person.address).add(person.name);}assert.deepEqual(// Convert the Map to an Array with key-value pairs,// so that we can compare it via assert.deepEqual().[...addressToNames],[[#{street:'1313 Mockingbird Lane',city:'Mockingbird Heights',},newSet(['Eddie','Herman']),],[#{street:'1630 Revello Drive',city:'Sunnydale',},newSet(['Dawn','Joyce']),],]);示例:有效地深度相等使用復合屬性值處理對象在下面的例子中 , 我們使用數組的方法.filter()(行(B))提取了地址等于address(行(A))的所有條目。
constpersons=[#{name:'Eddie',address:#{street:'1313 Mockingbird Lane',city:'Mockingbird Heights',},},#{name:'Dawn',address:#{street:'1630 Revello Drive',city:'Sunnydale',},},#{name:'Herman',address:#{street:'1313 Mockingbird Lane',city:'Mockingbird Heights',},},#{name:'Joyce',address:#{street:'1630 Revello Drive',city:'Sunnydale',},},];constaddress=#{// (A)street:'1630 Revello Drive',city:'Sunnydale',};assert.deepEqual(persons.filter(p=>p.address===address),// (B)[#{name:'Dawn',address:#{street:'1630 Revello Drive',city:'Sunnydale',},},#{name:'Joyce',address:#{street:'1630 Revello Drive',city:'Sunnydale',},},]);對象變了嗎?在處理緩存的數據(如下面例子中的previousData)時 , 內置深度相等可以讓我們有效地檢查數據是否發生了變化 。
letpreviousData;functiondisplayData(data){if(data=http://www.mnbkw.com/jxjc/189100/==previousData)return;// ···}displayData(#['Hello','world']);// 顯示displayData(#['Hello','world']);// 不顯示測試多數測試框架都支持深度相等 , 以檢查某個計算是否產生了預期的結果 。例如 , Node.js內置的assert模塊有一個函數叫deepEqual() 。有了復合原始值 , 就可以直接斷言:
functioninvert(color){return#{red:255-color.red,green:255-color.green,blue:255-color.blue,};}assert.ok(invert(#{red:255,green:153,blue:51})===#{red:0,green:102,blue:204});新語法的優缺點新語法的一個缺點是字符#已經在很多地方被占用了(比如私有字段) , 另外非數字字母字符多少顯得有點神秘 ??梢钥纯聪旅娴睦樱?br />
constdella=#{name:'Della',children:#[#{name:'Huey',},#{name:'Dewey',},#{name:'Louie',},],};優點是這個語法比較簡潔 。對于一個常用的結構 , 當然越簡單越好 。此外 , 一旦熟悉了這個語法之后 , 神秘感自然就會越來越淡 。除了特殊的字面量語法 , 還可以使用工廠函數:
constdella=Record({name:'Della',children:Tuple([Record({name:'Huey',}),Record({name:'Dewey',}),Record({name:'Louie',}),]),});如果JavaScript支持Tagged Collection Literals(https://github.com/zkat/proposal-collection-literals , 已撤銷) , 這個語法還可能有所改進:constdella=Record!{name:'Della',children:Tuple![Record!{name:'Huey',},Record!{name:'Dewey',},Record!{name:'Louie',},],};唉 , 即便使用更短的名字 , 結果看起來還是有點亂:constR=Record;constT=Tuple;constdella=R!{name:'Della',children:T![R!{name:'Huey',},R!{name:'Dewey',},R!{name:'Louie',},],};JSON與記錄和元組JSON.stringify()把記錄當成對象 , 把元組當成數組(遞歸) 。JSON.parseImmutable與JSON.parse()類似 , 但返回記錄而非對象 , 返回元組而非數組(遞歸) 。未來:類的實例會按值比較嗎?相比對象和數組 , 我其實更喜歡使用類作為一個數據容器 。因為它可以把名字添加到對象上 。為此 , 我希望將來會有一種類 , 它的實例不可修改且按值比較 。
【JS數字計時器 js實現計時器】假如我們還可以深度、非破壞性地更新那些包含由值類型的類產生的對象的數據 , 那就更好了 。
- 不屬于音頻文件格式有哪些 數字音頻文件格式有哪些
- java使用redis緩存 java實現redis緩存
- session共享如何實現 java session怎么共享
- js實現文字滾動 html文字滾動效果
- java中將字符串分割成數組 java把一串數字拆分成數組
- float類型保留兩位小數 float有幾位有效數字
- 個人數字證書怎么申請 個人數字證書申請流程
- 實現單點登錄 實現單點登錄的三種類型
- 設計網頁游戲 網絡游戲網站的設計與實現
- 有線數字電視機頂盒在哪里買 有線機頂盒在哪里買
