javascript網頁代碼大全,javascript計時器頻率是什麼

前言

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()會拋出異常。

使用記錄

const record = #{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});

使用元組

const tuple = #['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的元素)都被認為嚴格相等,所以映射和集合成會變得更有用。

接下來演示這些好處。

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

通過集合去重

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

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

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

如果是數組就辦不到了:

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

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

映射的複合鍵

因為對象是按標識比較的,所以在(非弱)映射中用對象作為鍵幾乎沒什麼用:

const m = new Map();

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

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

assert.equal(m.size, 2)

如果使用複合原始值就不一樣了:下面行(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 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']),

    ],

  ]);

示例:有效地深度相等

使用複合屬性值處理對象

在下面的例子中,我們使用數組的方法.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',

      },

    },

  ]);

對象變了嗎?

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

let previousData;

function displayData(data) {

  if (data === previousData) return;

  // ···

}

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

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

測試

多數測試框架都支持深度相等,以檢查某個計算是否產生了預期的結果。例如,Node.js內置的assert模塊有一個函數叫deepEqual()。有了複合原始值,就可以直接斷言:

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});

新語法的優缺點

新語法的一個缺點是字符#已經在很多地方被佔用了(比如私有字段),另外非數字字母字符多少顯得有點神秘。可以看看下面的例子:

const della = #{

  name: 'Della',

  children: #[

    #{

      name: 'Huey',

    },

    #{

      name: 'Dewey',

    },

    #{

      name: 'Louie',

    },

  ],

};

優點是這個語法比較簡潔。對於一個常用的結構,當然越簡單越好。此外,一旦熟悉了這個語法之後,神秘感自然就會越來越淡。

除了特殊的字面量語法,還可以使用工廠函數:

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,已撤銷),這個語法還可能有所改進:

const della = Record!{

  name: 'Della',

  children: Tuple![

    Record!{

      name: 'Huey',

    },

    Record!{

      name: 'Dewey',

    },

    Record!{

      name: 'Louie',

    },

  ],

};

唉,即便使用更短的名字,結果看起來還是有點亂:

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()把記錄當成對象,把元組當成數組(遞歸)。
  • JSON.parseImmutable與JSON.parse()類似,但返回記錄而非對象,返回元組而非數組(遞歸)。

未來:類的實例會按值比較嗎?

相比對象和數組,我其實更喜歡使用類作為一個數據容器。因為它可以把名字添加到對象上。為此,我希望將來會有一種類,它的實例不可修改且按值比較。

假如我們還可以深度、非破壞性地更新那些包含由值類型的類產生的對象的數據,那就更好了。

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/231176.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-10 18:46
下一篇 2024-12-10 18:46

相關推薦

發表回復

登錄後才能評論