<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【拓展】未來(lái)的JavaScript記錄與元組

          共 6226字,需瀏覽 13分鐘

           ·

          2020-08-20 21:38

          編者按:本文譯者李松峰,資深技術(shù)圖書譯者,翻譯出版過(guò)40余部技術(shù)及交互設(shè)計(jì)專著,現(xiàn)任360奇舞團(tuán)Web前端開發(fā)資深專家,360前端技術(shù)委員會(huì)委員、W3C AC代表。

          原文地址:https://2ality.com/2020/05/records-tuples-first-look.html

          原文作者:Dr. Axel Rauschmayer

          Dr. Axel Rauschmayer最近撰文介紹了還處于Stage1階段的兩個(gè)JavaScript新特性:記錄和元組。

          記錄和元組是一個(gè)新提案(Record & Tuple,https://github.com/tc39/proposal-record-tuple),建議為JavaScript增加兩個(gè)復(fù)合原始類型:

          • 記錄(Record),是不可修改的按值比較的對(duì)象

          • 元組(Tuple),是不可修改的按值比較的數(shù)組

          什么是按值比較

          當(dāng)前,JavaScript只有在比較原始值(如字符串)時(shí)才會(huì)按值比較(比較內(nèi)容):

          >?'abc'?===?'abc'

          true

          但在比較對(duì)象時(shí),則是按標(biāo)識(shí)比較(by identity),因此對(duì)象只與自身嚴(yán)格相等:

          >?{x:?1,?y:?4}?===?{x:?1,?y:?4}

          false

          >?['a',?'b']?===?['a',?'b']

          false

          “記錄和元組”的提案就是為了讓我們可以創(chuàng)建按值比較的復(fù)合類型值。

          比如,在對(duì)象字面量前面加一個(gè)井號(hào)(#),就可以創(chuàng)建一個(gè)記錄。而記錄是一個(gè)按值比較的復(fù)合值,且不可修改:

          >?#{x:?1,?y:?4}?===?#{x:?1,?y:?4}

          true

          如果在數(shù)組字面量前面加一個(gè)#,就可以創(chuàng)建一個(gè)元組,也就是可以按值比較且不可修改的數(shù)組:

          >?#['a',?'b']?===?#['a',?'b']

          true

          按值比較的復(fù)合值就叫復(fù)合原始值或者復(fù)合原始類型

          記錄和元組是原始類型

          使用typeof可以看出來(lái),記錄和元組都是原始類型:

          >?typeof?#{x:?1,?y:?4}

          'record'

          >?typeof?#['a',?'b']

          'tuple'

          記錄和元組的內(nèi)容有限制

          • 記錄:

            • 鍵必須是字符串

            • 值必須是原始值(包括記錄和元組)

          • 元組:

            • 元素必須是原始值(包括記錄和元組)

          把對(duì)象轉(zhuǎn)換為記錄和元組

          >?Record({x:?1,?y:?4})

          #{x:?1,?y:?4}

          >?Tuple.from(['a',?'b'])

          #['a',?'b']

          注意:這些都是淺層轉(zhuǎn)換。如果值樹結(jié)構(gòu)中有任何節(jié)點(diǎn)不是原始值,Record()和Tuple.from()會(huì)拋出異常。

          使用記錄

          const?record?=?#{x:?1,?y:?4};


          // 訪問(wèn)屬性

          assert.equal(record.y,?4);


          // 解構(gòu)

          const?{x}?=?record;

          assert.equal(x,?1);


          // 擴(kuò)展

          assert.ok(

          ??#{...record,?x:?3,?z:?9}?===?#{x:?3,?y:?4,?z:?9});

          使用元組

          const?tuple?=?#['a',?'b'];


          // 訪問(wèn)元素

          assert.equal(tuple[1],?'b');


          // 解構(gòu)(元組是可迭代對(duì)象)

          const?[a]?=?tuple;

          assert.equal(a,?'a');


          // 擴(kuò)展

          assert.ok(

          ??#[...tuple,?'c']?===?#['a',?'b',?'c']);


          // 更新

          assert.ok(

          ??tuple.with(0,?'x')?===?#['x',?'b']);

          為什么按值比較的值不可修改

          某些數(shù)據(jù)結(jié)構(gòu)(比如散列映射和搜索樹)有槽位,其中鍵的保存位置根據(jù)它們的值來(lái)確定。如果鍵的值改變了,那這個(gè)鍵通常必須放到不同的槽位。這就是為什么在JavaScript中可以用作鍵的值:

          • 要么按值比較且不可修改(原始值)

          • 要么按標(biāo)識(shí)比較且可修改(對(duì)象)

          復(fù)合原始值的好處

          復(fù)合原始值有如下好處。

          • 深度比較對(duì)象,這是一個(gè)內(nèi)置操作,可以通過(guò)如===來(lái)調(diào)用。

          • 共享值:如果對(duì)象是可修改的,為了安全共享就需要深度復(fù)制它的一個(gè)副本。而對(duì)于不可修改的值,就可以直接共享。

          • 數(shù)據(jù)的非破壞性更新:如果要修改復(fù)合值,由于一切都是不可修改的,所以就要?jiǎng)?chuàng)建一個(gè)可修改的副本,然后就可以放心地重用不必修改的部分。

          • 在Map和Set等數(shù)據(jù)結(jié)構(gòu)中使用:因?yàn)閮蓚€(gè)內(nèi)容相同的復(fù)合原始值在這門語(yǔ)言的任何地方(包括作為Map的鍵和作為Set的元素)都被認(rèn)為嚴(yán)格相等,所以映射和集合成會(huì)變得更有用。

          接下來(lái)演示這些好處。

          示例:集合與映射變得更有用

          通過(guò)集合去重

          有了復(fù)合原始值,即使是復(fù)合值(不是原始值那樣的原子值)也可以去重:

          >?[...new?Set([#[3,4],?#[3,4],?#[5,-1],?#[5,-1]])]

          [#[3,4],?#[5,-1]]

          如果是數(shù)組就辦不到了:

          >?[...new?Set([[3,4],?[3,4],?[5,-1],?[5,-1]])]

          [[3,4],?[3,4],?[5,-1],?[5,-1]]

          映射的復(fù)合鍵

          因?yàn)閷?duì)象是按標(biāo)識(shí)比較的,所以在(非弱)映射中用對(duì)象作為鍵幾乎沒什么用:

          const?m?=?new?Map();

          m.set({x:?1,?y:?4},?1);

          m.set({x:?1,?y:?4},?2);

          assert.equal(m.size,?2)

          如果使用復(fù)合原始值就不一樣了:下面行(A)創(chuàng)建的映射會(huì)保存地址(記錄)到人名的映射。

          const?persons?=?[

          ??#{

          ????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',

          ????},

          ??},

          ];


          const?addressToNames?=?new?Map();?// (A)

          for?(const?person?of?persons)?{

          ??if?(!addressToNames.has(person.address))?{

          ????addressToNames.set(person.address,?new?Set());

          ??}

          ??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',

          ??????},

          ??????new?Set(['Eddie',?'Herman']),

          ????],

          ????[

          ??????#{

          ????????street:?'1630 Revello Drive',

          ????????city:?'Sunnydale',

          ??????},

          ??????new?Set(['Dawn',?'Joyce']),

          ????],

          ??]);

          示例:有效地深度相等

          使用復(fù)合屬性值處理對(duì)象

          在下面的例子中,我們使用數(shù)組的方法.filter()(行(B))提取了地址等于address(行(A))的所有條目 。

          const?persons?=?[

          ??#{

          ????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',

          ????},

          ??},

          ];


          const?address?=?#{?// (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',

          ??????},

          ????},

          ??]);

          對(duì)象變了嗎?

          在處理緩存的數(shù)據(jù)(如下面例子中的previousData)時(shí),內(nèi)置深度相等可以讓我們有效地檢查數(shù)據(jù)是否發(fā)生了變化。

          let?previousData;

          function?displayData(data)?{

          ??if?(data?===?previousData)?return;

          ??// ···

          }


          displayData(#['Hello',?'world']);?// 顯示

          displayData(#['Hello',?'world']);?// 不顯示

          測(cè)試

          多數(shù)測(cè)試框架都支持深度相等,以檢查某個(gè)計(jì)算是否產(chǎn)生了預(yù)期的結(jié)果。例如,Node.js內(nèi)置的assert模塊有一個(gè)函數(shù)叫deepEqual()。有了復(fù)合原始值,就可以直接斷言:

          function?invert(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});

          新語(yǔ)法的優(yōu)缺點(diǎn)

          新語(yǔ)法的一個(gè)缺點(diǎn)是字符#已經(jīng)在很多地方被占用了(比如私有字段),另外非數(shù)字字母字符多少顯得有點(diǎn)神秘??梢钥纯聪旅娴睦樱?/p>

          const?della?=?#{

          ??name:?'Della',

          ??children:?#[

          ????#{

          ??????name:?'Huey',

          ????},

          ????#{

          ??????name:?'Dewey',

          ????},

          ????#{

          ??????name:?'Louie',

          ????},

          ??],

          };

          優(yōu)點(diǎn)是這個(gè)語(yǔ)法比較簡(jiǎn)潔。對(duì)于一個(gè)常用的結(jié)構(gòu),當(dāng)然越簡(jiǎn)單越好。此外,一旦熟悉了這個(gè)語(yǔ)法之后,神秘感自然就會(huì)越來(lái)越淡。

          除了特殊的字面量語(yǔ)法,還可以使用工廠函數(shù):

          const?della?=?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,已撤銷),這個(gè)語(yǔ)法還可能有所改進(jìn):

          const?della?=?Record!{

          ??name:?'Della',

          ??children:?Tuple![

          ????Record!{

          ??????name:?'Huey',

          ????},

          ????Record!{

          ??????name:?'Dewey',

          ????},

          ????Record!{

          ??????name:?'Louie',

          ????},

          ??],

          };

          唉,即便使用更短的名字,結(jié)果看起來(lái)還是有點(diǎn)亂:

          const?R?=?Record;

          const?T?=?Tuple;


          const?della?=?R!{

          ??name:?'Della',

          ??children:?T![

          ????R!{

          ??????name:?'Huey',

          ????},

          ????R!{

          ??????name:?'Dewey',

          ????},

          ????R!{

          ??????name:?'Louie',

          ????},

          ??],

          };

          JSON與記錄和元組

          • JSON.stringify()把記錄當(dāng)成對(duì)象,把元組當(dāng)成數(shù)組(遞歸)。

          • JSON.parseImmutable與JSON.parse()類似,但返回記錄而非對(duì)象,返回元組而非數(shù)組(遞歸)。

          未來(lái):類的實(shí)例會(huì)按值比較嗎?

          相比對(duì)象和數(shù)組,我其實(shí)更喜歡使用類作為一個(gè)數(shù)據(jù)容器。因?yàn)樗梢园衙痔砑拥綄?duì)象上。為此,我希望將來(lái)會(huì)有一種類,它的實(shí)例不可修改且按值比較。

          假如我們還可以深度、非破壞性地更新那些包含由值類型的類產(chǎn)生的對(duì)象的數(shù)據(jù),那就更好了。

          擴(kuò)展閱讀

          • 共享可修改狀態(tài)的問(wèn)題及如何避免:https://exploringjs.com/deep-js/ch_shared-mutable-state.html


          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情操逼影音 | 淫色淫香视频 | 日韩黄色成人视频 | 伊人色色网| 亚洲乱伦一区二区三区 |