跳到主要内容位置

Tuple 类型

提示

Tuple 类型目前处于 stage-2 阶段

Tuple 类型是一种类数组的结构,具有深度不可变的特点,即每个元素都是只读的,不能直接修改,对 tuple 的操作都会返回一个新的 tuple 实例。

在前端状态管理中,需要保证状态对象或数组是不可变的,否则会导致视图不刷新等异常情况,这是因为 JavaScript 对象是引用类型,保存的是对象的内存地址,如果修改了对象的内容,它的内存地址不会发生变化,这时如果在前端状态中,使用 === 判断,这里的的对象还是原来的,就不能探测到状态的变化,也就不能更新对应的视图了:


const state = {
arr: [1, 2, 3]
};
state.arr[0] = 4; // 并没返回新的数组

// 正确做法是:
state.arr = [4, ...arr.slice(1)]

前端库中,一般使用 immer.js 来实现不可变的数据,也有的库自行封装了一些逻辑,检测数组或对象的赋值操作,并返回全新的对象/数组。

Tuple 类型的出现就可以让我们避免使用第三方库,或者自行维护实现不可变数据的代码了。

使用 Tuple 类型

因为 Tuple 类型还处于 stage-2 阶段,所以我们还需要 babel 来体验。使用上一节配置好的 babel 项目,我们先安装 @babel/plugin-proposal-record-and-tuple@bloomberg/record-tuple-polyfill 依赖,分别是 babel对 Tuple 进行转译的插件和执行代码所必须的补充库:


yarn add --dev @babel/plugin-proposal-record-and-tuple @bloomberg/record-tuple-polyfill

之后,打开 babel.config.json,在 plugins 配置项中添加如下内容:

"plugins": [["@babel/plugin-proposal-record-and-tuple", {"syntaxType": "hash", "importPolyfill": true}]]

这里:

  • 我们添加了 @babel/plugin-proposal-record-and-tuple 插件。
  • 配置了 syntaxType,它是 Tuple 类型数据的语法类型,hash 表示使用 "#",它还可以取值 "bar"。
  • importPolyfill 会自动导入我们之前安装的 @bloomberg/record-tuple-polyfill,用于运行编译后的代码。

我们还可以在 package.json 里添加一个 start 命令,来运行编译后的代码:

  "scripts": {
"build": "babel index.js -o dist.js",
"start": "node dist.js"
},

示例

首先,看一下如何定义 Tuple 型数据。Tuple 类型的数据以 # 开头,后面的语法和数组一样,在 index.js 中,定义一个包含 1、2、3、4 这 4 个元素的 Tuple,之后 log 一下它的值:

const tuple = #[1, 2, 3, 4];
console.log(tuple);

运行 yarn build,转译代码,之后运行 yarn start 运行,可以看到命令行中打印出来了结果:

Tuple { '0': 1, '1': 2, '2': 3, '3': 4 }

这里 Tuple 类型就是 @bloomberg/record-tuple-polyfill 提供的目前 ECMAScript 语法可以兼容的版本,可以看到它使用了一个类数组的对象来表示 Tuple,当 Tuple 类型正式加入 ECMAScript 之后,会有专门的表示方法。

访问 Tuple 中的元素,和访问数组中的一样:

console.log(tuple[0]); // 1

因为 Tuple 是不可变的类型,所以不能直接修改它里边元素的值,例如下面这行代码会报错:

// TypeError: Cannot assign to read only property '0' of object '[object Tuple]'
tuple[0] = 5;

如果要修改 Tuple 元素的值,可以使用 with() 方法,例如把第 1 个元素的值改成 5,之后再把返回的结果保存到 newTuple 变量中,我们看看它和我们之前定义的 tuple 变量是不是指向了相同的内容地址:

const newTuple = tuple.with(0, 5);
console.log(newTuple, newTuple === tuple);

输出结果为:

Tuple { '0': 5, '1': 2, '2': 3, '3': 4 } false

可以看到成功的修改了第 1 个元素的值,并且返回的新 Tuple 和之前的也是不同的。

Tuple 类型也封装了和数组一样的方法,例如可以使用 map() 遍历所有 tuple 元素,并返回一个新的 Tuple:

// Tuple { '0': 2, '1': 4, '2': 6, '3': 8 }
console.log(tuple.map((x) => x * 2));

也可以使用针对数组的一些运算符,例如 spread:

const newTuple2 = #[...tuple, 5];

// Tuple { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
console.log(newTuple2);

或者解构赋值:

const [a, b] = tuple;
console.log(a, b); // 1, 2

需要注意的是,Tuple 类型里边只能包含基本类型数据,或者其他 Tuple 类型的数据,或者下节要介绍的 Record 数据,否则会破坏不可变特性,程序也会报错,例如下面的代码试图把一个普通的数组放到 Tuple 类型中,这是不可以的:

const newTuple2 = #[...tuple, 5, [6, 7]]; // 错误

源码:https://github.com/zxuqian/javascript-book-examples/tree/master/additional_content/new_features/02-tuple

提示

一系列的课程让你成为高级前端工程师。课程覆盖工作中所有常用的知识点和背后的使用逻辑,示例全部都为工作项目简化而来,学完即可直接上手开发!

即使你已经是高级前端工程师,在课程里也可能会发现新的知识点和技巧,让你的工作更加轻松!

《React 完全指南》课程,包含 React、React Router 和 Redux 详细介绍,所有示例改编自真实工作代码。点击查看详情。

《Vue 3.x 全家桶完全指南与实战》课程,包括 Vue 3.x、TypeScript、Vue Router 4.x、Vuex 4.x 所有初级到高级的语法特性详解,让你完全胜任 Vue 前端开发的工作。点击查看详情。

《React即时通信UI实战》课程,利用 Storybook、Styled-components、React-Spring 打造属于自己的组件库。

《JavaScript 基础语法详解》本人所著图书,包含 JavaScript 全面的语法知识和新特性, 可在京东、当当、淘宝等各大电商购买