ECMAScript 2024(ES15)将带来超实用的新特性

news2024/12/24 8:44:54

ECMAScript语言规范每年都会进行一次更新,而备受期待的 ECMAScript 2024 将于 2024 年 6 月正式亮相。目前,ECMAScript 2024 的候选版本已经发布,为我们带来了一系列实用的新功能。接下来,就让我们一起先睹为快吧!
在这里插入图片描述
全文概览:

  • Promise.withResolvers
  • Object.groupBy / Map.groupBy
  • ArrayBuffer.prototype.resize
  • ArrayBuffer.prototype.transfer
  • String.prototype.isWellFormed
  • String.prototype.toWellFormed
  • Atomics.waitAsync
  • 正则表达式 v 标志

Promise.withResolvers()

Promise.withResolvers() 允许创建一个新的 Promise,并同时获得 resolve 和 reject 函数。这在某些场景下非常有用,特别是当需要同时访问到 Promise 的 resolve 和 reject 函数时。

Promise.withResolvers() 完全等同于以下代码:

let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

通常,当创建一个新的 Promise 时,会传递一个执行器函数给 Promise 构造函数,这个执行器函数接收两个参数:resolve 和 reject 函数。但在某些情况下,可能想要在 Promise 创建之后仍然能够访问到这两个函数。这就是 Promise.withResolvers() 的用武之地。

使用 Promise.withResolvers() 的一个例子:

const { promise, resolve, reject } = Promise.withResolvers();  
  
// 在这里可以使用 resolve 和 reject 函数  
setTimeout(() => resolve('成功!'), 1000);  
  
promise.then(value => {  
  console.log(value); // 输出: 成功!  
});

在这个例子中,首先通过 Promise.withResolvers() 创建了一个新的 Promise,并同时获得了 resolve 和 reject 函数。然后,在 setTimeout 回调中使用 resolve 函数来解析这个 Promise。最后,添加了一个 .then 处理程序来处理 Promise 解析后的值。

使用 Promise.withResolvers() 关键的区别在于解决和拒绝函数现在与 Promise 本身处于同一作用域,而不是在执行器中被创建和一次性使用。这可能使得一些更高级的用例成为可能,例如在重复事件中重用它们,特别是在处理流和队列时。这通常也意味着相比在执行器内包装大量逻辑,嵌套会更少。

这个功能对于那些需要更细粒度控制 Promise 的状态,或者在 Promise 创建后仍然需要访问 resolve 和 reject 函数的场景来说非常有用。

Object.groupBy / Map.groupBy

Object.groupBy 和 Map.groupBy 方法用于数组分组。

假设有一个由表示人员的对象组成的数组,需要按照年龄进行分组。可以使用forEach循环来实现,代码如下:

const people = [
  { name: "Alice", age: 28 },
  { name: "Bob", age: 30 },
  { name: "Eve", age: 28 },
];

const peopleByAge = {};

people.forEach((person) => {
  const age = person.age;
  if (!peopleByAge[age]) {
    peopleByAge[age] = [];
  }
  peopleByAge[age].push(person);
});

console.log(peopleByAge);

输出结果如下:

{
  "28": [{"name":"Alice","age":28}, {"name":"Eve","age":28}],
    "30": [{"name":"Bob","age":30}]
}

也可以使用reduce方法:

const peopleByAge = people.reduce((acc, person) => {
  const age = person.age;
  if (!acc[age]) {
    acc[age] = [];
  }
  acc[age].push(person);
  return acc;
}, {});

无论哪种方式,代码都略显繁琐。每次都要检查对象,看分组键是否存在,如果不存在,则创建一个空数组,并将项目添加到该数组中。

使用 Object.groupBy 分组

可以通过以下方式来使用新的Object.groupBy方法:

const peopleByAge = Object.groupBy(people, (person) => person.age);

可以看到,代码非常简洁!

不过需要注意,使用Object.groupBy方法返回一个没有原型(即没有继承任何属性和方法)的对象。这意味着该对象不会继承Object.prototype上的任何属性或方法,例如hasOwnProperty或toString等。虽然这样做可以避免意外覆盖Object.prototype上的属性,但也意味着不能使用一些与对象相关的方法。

const peopleByAge = Object.groupBy(people, (person) => person.age);
console.log(peopleByAge.hasOwnProperty("28"));
// TypeError: peopleByAge.hasOwnProperty is not a function

在调用Object.groupBy时,传递给它的回调函数应该返回一个字符串或 Symbol 类型的值。如果回调函数返回其他类型的值,它将被强制转换为字符串。

在这个例子中,回调函数返回的是一个数字类型的age属性值,但由于Object.groupBy方法要求键必须是字符串或 Symbol 类型,所以该数字会被强制转换为字符串类型。

console.log(peopleByAge[28]);
// => [{"name":"Alice","age":28}, {"name":"Eve","age":28}]
console.log(peopleByAge["28"]);
// => [{"name":"Alice","age":28}, {"name":"Eve","age":28}]

使用 Map.groupBy 分组

Map.groupBy和Object.groupBy几乎做的是相同的事情,只是返回的结果类型不同。Map.groupBy返回一个Map对象,而不是像Object.groupBy返回一个普通的对象。

const ceo = { name: "Jamie", age: 40, reportsTo: null };
const manager = { name: "Alice", age: 28, reportsTo: ceo };

const people = [
  ceo
  manager,
  { name: "Bob", age: 30, reportsTo: manager },
  { name: "Eve", age: 28, reportsTo: ceo },
  ];

const peopleByManager = Map.groupBy(people, (person) => person.reportsTo);

这里根据人的汇报上级将他们进行了分组。如果想通过对象来从这个Map中获取数据,那么要求这些对象具有相同的身份或引用。这是因为Map在比较键时使用的是严格相等(===),只有两个对象具有相同的引用,才能被认为是相同的键。

peopleByManager.get(ceo);
// => [{ name: "Alice", age: 28, reportsTo: ceo }, { name: "Eve", age: 28, reportsTo: ceo }]
peopleByManager.get({ name: "Jamie", age: 40, reportsTo: null });
// => undefined

在上面的例子中,如果尝试使用与ceo对象类似的对象作为键去访问Map中的项,由于这个对象与之前存储在Map中的ceo对象不是同一个对象,所以无法检索到对应的值。

ArrayBuffer.prototype.resize

ArrayBuffer 实例的 resize() 方法将 ArrayBuffer 调整为指定的大小,以字节为单位,前提是该 ArrayBuffer 是可调整大小的并且新的大小小于或等于该 ArrayBuffer 的 maxByteLength。

const buffer = new ArrayBuffer(8, { maxByteLength: 16 });

console.log(buffer.byteLength);
// 8

if (buffer.resizable) {
  console.log("缓冲区大小是可调整的!");
  buffer.resize(12);
}

注意:

  • 如果 ArrayBuffer 已分离或不可调整大小,则抛出该错误。

  • 如果 newLength 大于该 ArrayBuffer 的 maxByteLength,则抛出该错误。

ArrayBuffer.prototype.transfer

transfer() 方法执行与结构化克隆算法相同的操作。它将当前 ArrayBuffer 的字节复制到一个新的 ArrayBuffer 对象中,然后分离当前 ArrayBuffer 对象,保留了当前 ArrayBuffer 的大小可调整性。

// 创建一个 ArrayBuffer 并写入一些字节
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
view[1] = 2;
view[7] = 4;

// 将缓冲区复制到另一个相同大小的缓冲区
const buffer2 = buffer.transfer();
console.log(buffer.detached); // true
console.log(buffer2.byteLength); // 8
const view2 = new Uint8Array(buffer2);
console.log(view2[1]); // 2
console.log(view2[7]); // 4

// 将缓冲区复制到一个更小的缓冲区
const buffer3 = buffer2.transfer(4);
console.log(buffer3.byteLength); // 4
const view3 = new Uint8Array(buffer3);
console.log(view3[1]); // 2
console.log(view3[7]); // undefined

// 将缓冲区复制到一个更大的缓冲区
const buffer4 = buffer3.transfer(8);
console.log(buffer4.byteLength); // 8
const view4 = new Uint8Array(buffer4);
console.log(view4[1]); // 2
console.log(view4[7]); // 0

// 已经分离,抛出 TypeError
buffer.transfer(); // TypeError: Cannot perform ArrayBuffer.prototype.transfer on a detached ArrayBuffer

String.prototype.isWellFormed()

isWellFormed() 方法返回一个表示该字符串是否包含单独代理项的布尔值。

JavaScript 中的字符串是 UTF-16 编码的。UTF-16 编码中的代理对是指:

在UTF-16编码中,代理对(Surrogate Pair)是一种特殊的编码机制,用于表示那些超出基本多文种平面(BMP)的Unicode字符。这些字符的Unicode码点高于U+FFFF,因此无法用一个16位的UTF-16码元来表示。为了解决这个问题,UTF-16引入了代理对机制。
代理对是由两个16位的码元组成的,一个称为高代理(或高代理码元),其码点范围在U+D800到U+DBFF之间;另一个称为低代理(或低代理码元),其码点范围在U+DC00到U+DFFF之间。这两个码元合在一起,可以表示一个超出BMP的Unicode字符。
例如,Unicode码点U+10000(这是BMP之外的第一个码点)在UTF-16中的编码就是高代理码元U+D800和低代理码元U+DC00的组合,即“D800 DC00”。同样,码点U+10001的UTF-16编码就是“D800 DC01”,以此类推。
通过这种方式,UTF-16编码能够完全表示所有Unicode字符,无论是BMP内的还是BMP外的。这种代理对机制是UTF-16编码方案的一个重要组成部分,使得UTF-16成为一种能够灵活处理各种语言字符的编码方式。

isWellFormed() 让你能够测试一个字符串是否是格式正确的(即不包含单独代理项)。由于引擎能够直接访问字符串的内部表示,与自定义实现相比 isWellFormed() 更高效。如果需要将字符串转换为格式正确的字符串,可以使用 toWellFormed() 方法。isWellFormed() 让你可以对格式正确和格式错误的字符串进行不同的处理,比如抛出一个错误或将其标记为无效。

const strings = [
  // 单独的前导代理
  "ab\uD800",
  "ab\uD800c",
  // 单独的后尾代理
  "\uDFFFab",
  "c\uDFFFab",
  // 格式正确
  "abc",
  "ab\uD83D\uDE04c",
];

for (const str of strings) {
  console.log(str.isWellFormed());
}
// 输出:
// false
// false
// false
// false
// true
// true

如果传递的字符串格式不正确, encodeURI 会抛出错误。可以通过使用 isWellFormed() 在将字符串传递给 encodeURI() 之前测试字符串来避免这种情况。

const illFormed = "https://example.com/search?q=\uD800";

try {
  encodeURI(illFormed);
} catch (e) {
  console.log(e); // URIError: URI malformed
}

if (illFormed.isWellFormed()) {
  console.log(encodeURI(illFormed));
} else {
  console.warn("Ill-formed strings encountered."); // Ill-formed strings encountered.
}

String.prototype.toWellFormed()

toWellFormed() 方法返回一个字符串,其中该字符串的所有单独代理项都被替换为 Unicode 替换字符 U+FFFD。

toWellFormed() 迭代字符串的码元,并将任何单独代理项替换为 Unicode 替换字符 U+FFFD。这确保了返回的字符串格式正确并可用于期望正确格式字符串的函数,比如 encodeURI。由于引擎能够直接访问字符串的内部表示,与自定义实现相比 toWellFormed() 更高效。

const strings = [
  // 单独的前导代理
  "ab\uD800",
  "ab\uD800c",
  // 单独的后尾代理
  "\uDFFFab",
  "c\uDFFFab",
  // 格式正确
  "abc",
  "ab\uD83D\uDE04c",
];

for (const str of strings) {
  console.log(str.toWellFormed());
}
// Logs:
// "ab�"
// "ab�c"
// "�ab"
// "c�ab"
// "abc"
// "ab😄c"

如果传递的字符串格式不正确, encodeURI 会抛出错误。可以先通过使用 toWellFormed() 将字符串转换为格式正确的字符串来避免这种情况。

const illFormed = "https://example.com/search?q=\uD800";

try {
  encodeURI(illFormed);
} catch (e) {
  console.log(e); // URIError: URI malformed
}

console.log(encodeURI(illFormed.toWellFormed())); // "https://example.com/search?q=%EF%BF%BD"

Atomics.waitAsync()

Atomics.waitAsync() 静态方法异步等待共享内存的特定位置并返回一个 Promise。与 Atomics.wait() 不同,waitAsync 是非阻塞的且可用于主线程。

下面来看一个简单的例子,给定一个共享的 Int32Array。

const sab = new SharedArrayBuffer(1024);
const int32 = new Int32Array(sab);

令一个读取线程休眠并在位置 0 处等待,预期该位置的值为 0。result.value 将是一个 promise。

const result = Atomics.waitAsync(int32, 0, 0, 1000);
// { async: true, value: Promise {<pending>} }

在该读取线程或另一个线程中,对内存位置 0 调用以令该 promise 解决为 “ok”。

Atomics.notify(int32, 0);
// { async: true, value: Promise {<fulfilled>: 'ok'} }

如果它没有解决为 “ok”,则共享内存该位置的值不符合预期(value 将是 “not-equal” 而不是一个 promise)或已经超时(该 promise 将解决为 “time-out”)。

正则表达式 v 标志

RegExp v 标志是 u 标志的超集,并提供了另外两个功能:

  • 字符串的 Unicode 属性: 通过 Unicode 属性转义,可以使用字符串的属性。
const re = /^\p{RGI_Emoji}$/v;

// 匹配仅包含 1 个代码点的表情符号:
re.test('⚽'); // '\u26BD'
// → true ✅

// 匹配由多个代码点组成的表情符号:
re.test('👨🏾‍⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
// → true ✅
  • 设置符号: 允许在字符类之间进行集合操作。
const re = /[\p{White_Space}&&\p{ASCII}]/v;
re.test('\n'); // → true
re.test('\u2028'); // → false

在JavaScript的正则表达式中,u 标志表示“Unicode”模式。当你在正则表达式中使用这个标志时,它会将模式视为Unicode序列的集合,而不仅仅是一组ASCII字符。这意味着正则表达式会正确地处理四个字节的UTF-16编码。

具体来说,u 标志有以下几个作用:

正确处理Unicode字符:不使用 u 标志时,正则表达式可能无法正确处理Unicode字符,尤其是那些超出基本多文种平面(BMP)的字符。使用 u 标志后,你可以匹配和处理任何有效的Unicode字符。

改变量词的行为:在Unicode模式下,量词(如 *、+、?、{n}、{n,}、{n,m})会匹配任何有效的Unicode字符,而不仅仅是ASCII字符。

允许使用\p{…} 和 \P{…}:这两个是Unicode属性转义,允许匹配或不匹配具有特定Unicode属性的字符。例如,\p{Script=Arabic} 会匹配任何阿拉伯脚本的字符。

正确处理Unicode转义:在Unicode模式下,你可以使用 \u{…} 来表示一个Unicode字符,其中 {…} 是一个四位的十六进制数。

修正了某些正则表达式方法的行为:例如,String.prototype.match()、String.prototype.replace()、String.prototype.search() 和 RegExp.prototype.exec() 等方法在Unicode模式下会返回更准确的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1457745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技云报道:云原生是大模型“降本增效”的解药吗?

科技云报道原创。 在过去一两年里&#xff0c;以GPT和Diffusion model为代表的大语言模型和生成式AI&#xff0c;将人们对AI的期待推向了一个新高峰&#xff0c;并吸引了千行百业尝试在业务中利用大模型。 国内各家大厂在大模型领域展开了激烈的军备竞赛&#xff0c;如&#…

iconfont的使用(最详解)

目录 一、Iconfont是什么&#xff1f; 二、Iconfont如何使用 1.官网注册 2.新建项目 3.项目中使用 Unicode方式 Font class方式 Symbol方式 三、总结 一、Iconfont是什么&#xff1f; iconfont是阿里旗下的一套图标库&#xff0c;UI设计师设计号图标后&#xff0c;会…

用pandas做简单策略回测

一&#xff0c;RSI策略 数据&#xff1a; 代码 import pandas as pd# 读取贵州茅台股票历史交易数据 df pd.read_csv(贵州茅台股票历史交易数据.csv) missing_values df.isnull().sum()# print("缺失值数量&#xff1a;") # print(missing_values)# 计算RSI指标 …

【LeetCode: 590. N 叉树的后序遍历 + DFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

机器学习中梯度下降法的缺点

机器学习中的梯度下降法是一种寻找函数最小值的优化算法&#xff0c;广泛应用于训练各种模型&#xff0c;尤其是在深度学习中。尽管其应用广泛&#xff0c;但梯度下降法也存在一些不可忽视的缺点&#xff1a; 1. 局部最小值和鞍点 局部最小值问题&#xff1a; 对于非凸函数&a…

Rofin罗芬Laser激光DQ80设备操作说明书

Rofin罗芬Laser激光DQ80设备操作说明书

Elasticsearch:什么是 kNN?

kNN - K-nearest neighbor 定义 kNN&#xff08;即 k 最近邻算法&#xff09;是一种机器学习算法&#xff0c;它使用邻近度将一个数据点与其训练并记忆的一组数据进行比较以进行预测。 这种基于实例的学习为 kNN 提供了 “惰性学习&#xff08;lazy learning&#xff09;” 名…

【大模型】大模型基础知识

几个常用术语 模型公式参数 K矩阵&#xff1a;字典 V矩阵&#xff1a;关键字的权重数值 AGI&#xff1a;通用人工智能 分词、词性关联、词性标注、知识图谱。分词操作是AI的开发&#xff0c;但是离AGI越来越远。自注意力机制的核心思想是摒弃分词等操作&#xff0c;面向更通…

机器人初识 —— 电机传动系统

一、背景 波士顿动力公司开发的机器人&#xff0c;其电机传动系统是其高性能和动态运动能力的核心部分。电机传动系统通常包括以下几个关键组件&#xff1a; 1. **电动马达**&#xff1a;波士顿动力的机器人采用了先进的电动马达作为主要的动力源&#xff0c;如伺服电机或步进…

Netty Review - NIO空轮询及Netty的解决方案源码分析

文章目录 Pre问题说明NIO CodeNetty是如何解决的&#xff1f;源码分析入口源码分析selectCntselectRebuildSelector Pre Netty Review - ServerBootstrap源码解析 Netty Review - NioServerSocketChannel源码分析 Netty Review - 服务端channel注册流程源码解析 问题说明 N…

探索AI视频生成新纪元:文生视频Sora VS RunwayML、Pika及StableVideo——谁将引领未来

探索AI视频生成新纪元&#xff1a;文生视频Sora VS RunwayML、Pika及StableVideo——谁将引领未来 sora文生视频&#xff0c;探索AI视频生成新纪元 由于在AI生成视频的时长上成功突破到一分钟&#xff0c;再加上演示视频的高度逼真和高质量&#xff0c;Sora立刻引起了轰动。在S…

vscode 开发代码片段插件

环境准备 node - 20v版本 &#xff0c;推荐使用nvm进行版本控制全局安装 "yo" 是 Yeoman 工具的命令行工具&#xff0c; npm i yo -g全局安装 generator-code 是一个 Yeoman 脚手架 gernerator-code npm i gernerator-code -g全局安装 npm install -g vsce官方文档 …

代码随想录算法训练营第55天 | 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费

最佳买卖股票时机含冷冻期 股票题做到这地步&#xff0c;可以发现分析每一天的可能状态与状态之间的转换是解决问题的关键。这道题中每一天可以有4种有效状态&#xff1a; 持有股票不持有股票&#xff0c;当天之前就已经卖出了股票不持有股票&#xff0c;当天卖出股票冷冻期状…

数字化转型导师坚鹏:政府数字化转型之数字化新技术解析与应用

政府数字化转型之数字化新技术解析与应用 课程背景&#xff1a; 数字化背景下&#xff0c;很多政府存在以下问题&#xff1a; 不清楚新技术的发展现状&#xff1f; 不清楚新技术的重要应用&#xff1f; 不清楚新技术的成功案例&#xff1f; 课程特色&#xff1a; 有…

计算机视觉的应用23-OpenAI发布的文本生成视频大模型Sora的原理解密

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用23-OpenAI发布的文本生成视频大模型Sora的原理解密。本文概况性地将Sora模型生成视频主要分为三个步骤&#xff1a;视频压缩网络、空间时间潜在补丁提取以及视频生成的Transformer模型。 文章目录…

专修戴尔R730xd服务器闪电灯 心跳亮黄灯故障

2024年开年第二天接到一个用户反馈说他公司有一台DELL PowerEdge R730xd服务器春节前由于市电问题意外断电关机了&#xff0c;刚好碰上春节就没去开机了&#xff0c;今天工厂开工服务器通电发现开不了机&#xff0c;且机器过了一会后报了2个黄灯错误&#xff0c;如下图&#xf…

【数据结构】每天五分钟,快速入门数据结构(一)——数组

目录 一.初始化语法 二.特点 三.数组中的元素默认值 四.时间复杂度 五.Java中的ArrayList类 可变长度数组 1 使用 2 注意事项 3 实现原理 4 ArrayList源码 5 ArrayList方法 一.初始化语法 // 数组动态初始化&#xff08;先定义数组&#xff0c;指定数组长度&#xf…

书生浦语大模型实战营-课程笔记(5)

LLM部署特点&#xff0c;内存开销大&#xff0c;TOKEN数量不确定 移动端竟然也可以部署LLM。之前以为只能在服务端部署&#xff0c;移动端作为客户端发起请求来调用大模型。 LMDeploy用于模型量化 模型量化&#xff1a;降低内存消耗 推理性能对比 量化主要作用&#xff1a;…

内存计算研究进展-技术架构

内存计算技术是一个宏观的概念&#xff0c;是将计算能力集成到内存中的技术统称。集成了内存计算技术的计算机系统不仅能直接在内存中执行部分计算&#xff0c;还能支持传统以CPU为核心的应用程序的执行。区别于内存计算&#xff0c;存算一体芯片将存储与计算相结合&#xff0c…

MySQL--SQL解析顺序

前言&#xff1a; 一直是想知道一条SQL语句是怎么被执行的&#xff0c;它执行的顺序是怎样的&#xff0c;然后查看总结各方资料&#xff0c;就有了下面这一篇博文了。 本文将从MySQL总体架构—>查询执行流程—>语句执行顺序来探讨一下其中的知识。 一、MySQL架构总览&a…