JavaScript 中的 Tuples 和 Records 是什么?
不变性是我们谈论函数式编程时经常出现的一个常用术语。 这是因为它是其核心原则之一。 当我们谈论不可变对象时,我们只是意味着一旦声明了一个变量,它的值就不能在以后进行更改。 例如:
const presents = ['🎁', '📦', '🎀', '💝', '🎄'];
// --- 可变解决方案 ---
// 我们略过 🎁
// presents 将等价于 ['📦', '🎀', '💝', '🎄'];
presents.shift();
// --- 不可变的解决方案 ---
// newPresents 相当于 📦 🎀 💝 🎄
// presents 依然为 ['🎁', '📦', '🎀', '💝', '🎄'];
const newPresents = presents.slice(1);
第一个解决方案改变了数组,而第二个解决方案创建了一个新的并保持原始数组不变。 在 JavaScript 中,我们没有真正的不可变对象,所以我们要么需要变通方法来实现安全,要么更糟糕的是,我们必须相信人们不会改变这些值。
现在有一个新的 ECMAScript 提案——目前处于第 2 阶段,因此这些实现可以改变——将引入两种新的不可变数据类型:Tuples和 Records。
Tuples(元组)
Tuples 和 Records 都具有相同的语法。 它们可以通过在对象和数组前面使用 #
前缀来定义,如下所示:
// 这是一个正常的数组
const arr = [];
// 这是一个 tuple
const tuple = #[];
使用 Tuples 时,需要注意一些规则:
- 数组中不能有空洞,例如:[1, ,2] 是不允许的;
- 它们只能包含原语或其他 Tuples 和 Records;
- 支持类似于 Arrays 的实例方法,但有一些变化;
例如,改变数组的操作被替换为返回新数组的新操作。 因此,例如:没有 push,我们可以使用 push 来返回一个带有推送值的新元组,或者使用 with 来更改给定索引处的值:
const tuple = #['🍄', '🍅', '🥕'];
// 都返回一个新元组
tuple.pushed('🥒'); // 返回 #['🍄', '🍅', '🥕', '🥒'];
tuple.with(0, '🌳'); // 返回 #['🌳', '🍅', '🥕']
我们还可以使用 Tuple.from() 从现有数组创建元组:
Tuple.from(['🍄', '🍅', '🥕']);
// 同样,我们可以将元组转换为普通数组:
Array.from(tuple);
当然,它们是不可变的,如果尝试更改它们的值或使用非原始值,它们会抛出错误:
const tuples = #['🍄', '🍅', '🥕'];
// TypeError: Callback to Tuple.prototype.map may only return primitives, Records or Tuples
tuples.map(tuple => new Button(tuple));
Records 记录
就像元组一样,记录也用哈希表示:
// 这是一个常规对象
const obj = { ... };
// 这是一个记录
const record = #{
tuple: #['🍄', '🍅', '🥕'] // Records 也会包含 元组(Tuples)
};
处理记录时,我们还需要牢记一些规则:
-
不能在记录中使用
__proto__
标识符 - 方法也是不允许的。 就像元组一样,它们只能包含原语。
要创建新记录,我们还可以选择在使用元组时使用 Record
或 Record.fromEntries
:
const record = Record({
mushroom: '🍄',
tomato: '🍅',
carrot: '🥕'
});
// 或者
const record = Record.fromEntries(#['🍄', '🍅', '🥕']);
而且由于它们是新的数据类型,因此在使用 typeof
运算符时会返回“record”:
typeof #{ ... } // 返回 "record"
typeof #[ ... ] // 返回 "tuple"
总结
除了上面提到的示例之外,我们可以在函数或循环中同时使用记录和元组,就像我们通常使用常规对象一样。
如果你现在想要不可变,最简单的方法是使用 Immutable-js。 还可以使用 Object.freeze 实现部分不变性,但是,它只会冻结直接子级。 因此,我们仍然可以更改深度嵌套的属性:
const obj = Object.freeze({
a: 1,
b: {
c: 2
}
});
// ✅ Won't work
obj.a = 10;
// ❌ Will be changed to 20
obj.b.c = 20;
当然,你也可以通过 plugin-syntax-record-and-tuple
插件将它与 Babel 一起使用。
大家是否已经使用过元组(Tuples)和记录(Records)? 欢迎大家在下面留言一起来讨论!
相关文章
Do you understand JavaScript closures?
发布时间:2025/02/21 浏览次数:108 分类:JavaScript
-
The function of a closure can be inferred from its name, suggesting that it is related to the concept of scope. A closure itself is a core concept in JavaScript, and being a core concept, it is naturally also a difficult one.
Do you know about the hidden traps in variables in JavaScript?
发布时间:2025/02/21 浏览次数:178 分类:JavaScript
-
Whether you're just starting to learn JavaScript or have been using it for a long time, I believe you'll encounter some traps related to JavaScript variable scope. The goal is to identify these traps before you fall into them, in order to av
How much do you know about the Prototype Chain?
发布时间:2025/02/21 浏览次数:150 分类:JavaScript
-
The prototype chain can be considered one of the core features of JavaScript, and certainly one of its more challenging aspects. If you've learned other object-oriented programming languages, you may find it somewhat confusing when you start
如何在 JavaScript 中合并两个数组而不出现重复的情况
发布时间:2024/03/23 浏览次数:86 分类:JavaScript
-
本教程介绍了如何在 JavaScript 中合并两个数组,以及如何删除任何重复的数组。