JavaScript 中的 Tuple 和 Record

Eason Lin
7 min readMay 5, 2024

--

Photo by Daniel K Cheung on Unsplash

大家好,這篇文我們來看一下 Tuple 和 Record 這兩種資料型別。截至這篇文(2024/05/05)撰寫的當下,還沒有任何主流瀏覽器有實作出 Tuple 和 Record,所以千萬別打開 Console 直接試範例的語法,只會得到一堆錯誤,文末會提供由其他開發者實作的 Playground 供體驗使用。根據一些文章指出,這兩個資料型別很可能在被發佈在 ES2024 當中。

對專精於 JavaScript 的開發者來說,如果沒有使用過 TypeScript,可能會不知道 Tuple 或 Record 是什麼,因此就讓我們先一起認識一下這兩種資料型別。

Tuple

Tuple 中文翻譯為「元組」,類似於陣列,是一種有序列表。宣告的方式會像是這樣子:

const tuple = #[1, 2, 3];

應該可以看出宣告方式也類似於陣列,但在前面多了 #。元組與陣列的主要差異在於元組不可變,也僅能存放不可變的值。因此以下的元組都會是合法的:

let a;
// 布林值, undefined, 數字
const tuple1 = #[true, a, 3];
// null, 字串和 Symbol
const tuple2 = #[null, 'some words', Symbol("foo")];

值得注意的是,由於元組本身並不可變,因此可以在元組中存放元組:

const tuple3 = #[#[1,2,3], 2, 3]
console.log(tuple3[0]) // Tuple {0: 1, 1: 2, 2: 3}

而以下的元組則不合法:

// 包含了陣列,陣列本身可變
const tuple1 = #[[1, 2, 3], undefined, 3];
// 包含了物件,物件本身可變
const tuple2 = #[{ a: 1 }, undefined, 3];

以下的元組合法,但異動了其中的值因此會報錯:

const tuple = #[true, '', 3];
tuple[1] = 'modified'; // Error occured

對其新增值也會報錯:

const tuple3 = #[#[1,2,3], 2, 3];
tuple3.push(4); // Error occured

可使用元組的情境

比較直覺可以想到使用元組的情境是類似映射表的應用。由於元組不可變,因此在使用上就可以肯定它不會在程式執行的過程被修改,例如:

// someFile.js
export const weekNames = #['週一', '週二', '週三', '週四', '週五', '週六', '週日'];

// anotherFile.js
import { weekNames } from 'path/to/someFile.js';
// 其他邏輯...
const mondayChineseName = weekNames[0];
// 其他邏輯...

Record

Record 類似於物件,和元組相同,Record 也是不可變的。也由於不可變,其僅能存放不可變的值(當然包含了 Record 本身和元組)。以下的 Record 都是合法的:

const record1 = #{
name: "John",
gender: "male",
};

const record2 = #{
a: 1,
b: undefined,
};

const record3 = #{
foo: true,
bar: null,
};

const record4 = #{
first: Symbol('aa'),
second: Symbol('bb'),
};

const record5 = {
a: #{
b: #[1, 2, 3],
},
c: #[4, 5, 6],
};

以下的 Record 則不合法:


const record1 = #{
a: {
b: 1,
},
}

const record2 = #{
c: [1, 2, 3]
}

可使用 Record 的情境

當我們使用一些前端框架或庫時,就可以使用 Record 存放一些不可變的狀態,避免調整時不小心的竄改。

其他特性

如果將每一個值都相同但宣告為不同變數的元組進行比較:

import { Record, Tuple } from "@bloomberg/record-tuple-polyfill";
const log = console.log;
const a = #[1, 2, 3];
const b = #[1, 2, 3];
log(a === b);

這裡會回傳 true

import { Record, Tuple } from "@bloomberg/record-tuple-polyfill";
const log = console.log;
const a = #[1, 2, 3];
const b = #[1, 2, 4];
log(a === b);

這裡則會回傳 false。當然,如果存入的是 Symbol,因為每一個從 Symbol() 回傳的值都是獨一無二的,所以:

import { Record, Tuple } from "@bloomberg/record-tuple-polyfill";
const log = console.log;
const a = #[1, 2, Symbol('foo')];
const b = #[1, 2, Symbol('foo')];
log(a === b);

會回傳 false

同樣的邏輯也適用於 Record:

import { Record, Tuple } from "@bloomberg/record-tuple-polyfill";
const log = console.log;
const a = #{
c: 1,
d: 2,
}
const b = #{
c: 1,
d: 2
};
log(a === b); // true

--

--