首页 >web前端 >js教程 >记录和元组:JavaScript的新不成熟的数据类型

记录和元组:JavaScript的新不成熟的数据类型

Christopher Nolan
Christopher Nolan原创
2025-02-12 08:25:09235浏览

Records and Tuples: JavaScript's New Immutable Data Types

JavaScript 的记录和元组:不可变数据结构的未来

JavaScript 即将迎来两种新的不可变数据类型:记录和元组。它们目前处于 TC39 标准审批流程的第 2 阶段,还在不断完善中,尚未在任何浏览器或运行时环境中可用,但预计在明年内就会有可用的实现版本。它们旨在解决开发者在使用数组和对象时面临的一些棘手问题。

关键要点

  • 记录和元组是 JavaScript 中正在开发的新的不可变数据类型,预计在明年内可用。它们旨在解决开发者面临的挑战,例如确保函数不会有意或无意地更改数组或对象中保存的值。
  • 元组是深度不可变的类似数组的数据结构。它们不能有未设置的值,只能设置基本类型、其他元组或记录。元组是基本类型,因此可以按值与其他元组进行深度比较。
  • 记录是深度不可变的类似对象的数据结构。它们必须使用字符串属性名,并且只能使用基本类型、其他元组或记录设置值。记录可以与其他记录进行深度比较,并且属性顺序无关紧要。

const 的局限性

经验丰富的 JavaScript 开发者都知道,尽可能使用 const 声明变量是最佳实践。它使变量变得不可变。值不能更改,因此您需要处理的问题更少。

不幸的是,const 只能使基本值不可变(字符串、数字、BigInt、布尔值、符号和未定义)。您不能重新赋值数组或对象,但它们包含的值和属性可以修改。例如:

<code class="language-javascript">// 数组常量
const myArray = [1, 2, 3];

// 更改数组值
myArray[0] = 99;
myArray.push(42);

console.log(myArray); // [ 99, 2, 3, 42 ]

myArray = 'change'; // 错误!</code>

对象也是如此:

<code class="language-javascript">// 对象常量
const myObj = { a: 1, b: 2, c: 3 };

// 更改对象属性
myObj.a = 99;
myObj.d = 42;

console.log(myObj); // { a: 99, b: 2, c: 3, d: 42 }

myObj = 'change'; // 错误!</code>

Object.freeze() 方法可以提供帮助,但它只对对象的直接子属性应用浅冻结:

<code class="language-javascript">const myObj = { a: 1, b: 2, c: { v: 3 } };
Object.freeze(myObj);

myObj.a = 99; // 静默忽略
myObj.c.v = 99; // 可以正常工作

console.log(myObj); // { a: 1, b: 2, c: { v: 99 } }</code>

因此,很难保证函数不会有意或无意地更改数组或对象中保存的值。开发人员要么听天由命,要么传递变量的克隆版本——(这也有其自身的挑战)。

不一致的比较

当开发人员尝试进行看似合理的对象或数组比较时,可能会出现更多混乱:

<code class="language-javascript">const str = 'my string';
console.log(str === 'mystring');  // false

const num = 123;
console.log(num === 123);         // true

const arr = [1, 2, 3];
console.log(arr === [1, 2, 3]);   // false

const obj = { a: 1 };
console.log(obj === { a: 1 });    // false</code>

只有基本类型可以按值比较。对象和数组是按引用传递和比较的。只有当两个变量指向内存中的同一项时,它们才相等:

<code class="language-javascript">const a = [1, 2];

const b = a;
b.push(3);

console.log(a === b); // true

// 原始数组已更改
console.log(a); // [1, 2, 3]</code>

深度比较两个对象或数组需要一个递归比较函数来依次评估每个值。即使那样,您也可能会遇到日期或函数等类型的问题,这些类型可能以不同的方式存储。

元组:不可变的类似数组的数据结构

元组是深度不可变的类似数组的数据结构。它们实际上是复合基本类型,在普通数组语法前面使用 # 修饰符来标识:

<code class="language-javascript">// 数组常量
const myArray = [1, 2, 3];

// 更改数组值
myArray[0] = 99;
myArray.push(42);

console.log(myArray); // [ 99, 2, 3, 42 ]

myArray = 'change'; // 错误!</code>

或者,新的 Tuple.from() 方法可以从数组创建一个元组:

<code class="language-javascript">// 对象常量
const myObj = { a: 1, b: 2, c: 3 };

// 更改对象属性
myObj.a = 99;
myObj.d = 42;

console.log(myObj); // { a: 99, b: 2, c: 3, d: 42 }

myObj = 'change'; // 错误!</code>

与标准数组不同,元组必须满足以下要求:

  1. 它们不能有值为 undefined 的空位。例如,#[1,,,4] 是无效的。
  2. 它们只能设置基本类型、其他元组或记录。不允许使用数组、对象或函数等类型:
<code class="language-javascript">const myObj = { a: 1, b: 2, c: { v: 3 } };
Object.freeze(myObj);

myObj.a = 99; // 静默忽略
myObj.c.v = 99; // 可以正常工作

console.log(myObj); // { a: 1, b: 2, c: { v: 99 } }</code>

由于元组是基本类型,因此可以按值与其他元组进行深度比较:

<code class="language-javascript">const str = 'my string';
console.log(str === 'mystring');  // false

const num = 123;
console.log(num === 123);         // true

const arr = [1, 2, 3];
console.log(arr === [1, 2, 3]);   // false

const obj = { a: 1 };
console.log(obj === { a: 1 });    // false</code>

请注意,如果元组包含单个值,则可以使用较宽松的 == 运算符进行比较。例如:

<code class="language-javascript">const a = [1, 2];

const b = a;
b.push(3);

console.log(a === b); // true

// 原始数组已更改
console.log(a); // [1, 2, 3]</code>

记录:不可变的类似对象的数据结构

记录是深度不可变的类似对象的数据结构。同样,它们是复合基本类型,在普通对象语法前面使用 # 修饰符来标识:

<code class="language-javascript">// 新的元组
const t1 = #[1, 2, 3];
const t2 = #[1, 2, #[3, 4]];</code>

或者,新的 Record() 构造函数可以从对象创建一个记录:

<code class="language-javascript">// 从数组创建新的元组
const t3 = Tuple.from([1, 2, 3]);</code>

或者 Record.fromEntries() 方法可以从一系列数组或元组键值对创建一个记录:

<code class="language-javascript">const t4 = #[new Date()]; // 错误(设置一个对象)
const t5 = #[1, 2, [3, 4]]; // 错误(设置一个数组)</code>

与标准对象不同,记录必须满足以下要求:

  1. 它们必须使用字符串属性名。例如,#{ Symbol(): 1 } 是无效的。
  2. 它们只能使用基本类型、其他元组或记录设置值。不允许使用数组、对象或函数等类型:
<code class="language-javascript">const t6 = #[1, 2];

console.log(t6 === #[1, 2]); // true</code>

记录可以与其他记录进行深度比较,并且属性顺序无关紧要:

<code class="language-javascript">const t7 = #[99];

console.log(t7 == #[99]); // true
console.log(t7 == 99);    // true
console.log(t7 == '99');  // true

// 元组不能与数组比较
console.log(t7 == [99]);  // false</code>

记录只能与其他记录进行比较,因此使用 ===== 运算符没有区别。但是,可以提取 Object.keys()Object.values() 用于特定比较。例如:

<code class="language-javascript">// 新的记录
const r1 = #{ a: 1, b: 2 };
const r2 = #{
  a: 1,
  b: #{ c: 2 }, // 子记录
  d: #[3, 4]  // 子元组
};</code>

不可变更新

元组和记录听起来像是复杂计算机科学术语,但它们最终允许在 JavaScript 中进行强大的不可变数据存储和比较。

JavaScript 中的记录和元组常见问题解答

什么是 JavaScript 中的记录?JavaScript 中的记录是 ECMAScript 2022 (ES12) 中引入的一种类似对象的结构。它旨在表示具有命名属性的数据,并且默认情况下是不可变的,因此适合用作数据结构,其中值在创建后不会更改。

记录与普通的 JavaScript 对象有何不同?与普通的对象不同,JavaScript 中的记录默认情况下是不可变的,并且一旦设置,其属性就不能修改。记录的设计也更可预测,结构更严格,因此适合用作数据容器。

什么是 JavaScript 中的元组?JavaScript 中的元组是一个有序的元素集合,其中每个元素可以是不同类型。元组是不可变的,并且长度固定,提供了一种表示和处理特定顺序的固定数量值的方法。

记录和元组如何增强代码的可读性和可维护性?记录和元组可以通过提供更声明性和结构化的方式来表示数据来增强代码的可读性。不可变性方面也有助于防止意外修改,从而提高代码的可维护性。

使用记录和元组时是否存在任何性能方面的考虑?记录和元组作为不可变数据结构,在某些情况下可以提高性能。但是,必须考虑浏览器支持和具体的用例,因为性能影响可能会因 JavaScript 运行时的实现细节而异。

如何在没有原生支持的环境中为记录和元组提供 polyfill?截至我的上次更新,可以使用 polyfill 或转译器在没有原生支持的环境中模拟记录和元组。但是,建议随时了解 JavaScript 标准和工具的更新,因为生态系统在不断发展。

以上是记录和元组:JavaScript的新不成熟的数据类型的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn