ECMAScript 2023 将有哪些更新?

简介: ECMAScript 2023 将有哪些更新?

ECMAScript 规范每年都会更新一次,正式标准化 JavaScript 语言的 ECMAScript 的下一次年度更新将在 2023 年 6 月左右获得批准,这将是 ECMAScript 的第 14 版。下面是 ECMAScript 提案中已完成并预计在 ECMAScript 2023 发布的功能!

提案 作者 TC39 会议记录 预计发布年份
Array find from last Wenlu Wang June 2022 2023
Hashbang Grammar Bradley Farias July 2022 2023
Change Array by Copy Ashley Claymore、Robin Ricard March 2022 2023
Symbols as WeakMap keys Daniel Ehrenberg、Richard Button、Robin Ricard、Leo Balter、Rick Waldron、Caridy Patiño January 2023 2023

已完成的提案github.com/tc39/propos…

下面就来看看这些功能都是怎么用的!

1. 从尾到头搜索数组

(1)概述

在 JavaScript 中,通过 find()findIndex()  查找数组中的值是一种常见做法。不过,这些方法从数组的开始进行遍历:

const array = [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];
array.find(elem => elem.v > 3); // {v: 4}
array.findIndex(elem => elem.v > 3); // 3

如果要从数组的末尾开始遍历,就必须反转数组并使用上述方法。这样做就需要一个额外的数组操作。findLast()findLastIndex() 的就解决了这一问题。提出这两个方法的一个重要原因就是:语义

(2)使用

它们的用法和find()findIndex()类似,唯一不同的是它们是 从后向前 遍历数组,这两个方法适用于数组类数组

  • findLast() 会返回第一个查找到的元素,如果没有找到,就会返回 undefined
  • findLastIndex() 会返回第一个查找到的元素的索引。如果没有找到,就会返回 -1;
const array = [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];
array.findLast(elem => elem.v > 3); // {v: 5}
array.findLastIndex(elem => elem.v > 3); // 4
array.findLastIndex(elem => elem.v > 5); // undefined

(3)polyfill

下面来实现一下这两个方法:

  • Array.prototype.findLast
Array.prototype.findLast = function(arr, callback, thisArg) {
  for (let index = arr.length - 1; index >= 0; index--) {
    const value = arr[index];
    if (callback.call(thisArg, value, index, arr)) {
      return value;
    }
  }
  return undefined;
}
  • Array.prototype.findLastIndex
Array.prototype.findLastIndex = function(arr, callback, thisArg) {
  for (let index = arr.length - 1; index >= 0; index--) {
    const value = arr[index];
    if (callback.call(thisArg, value, index, arr)) {
      return index;
    }
  }
  return -1;
}

(4)参考源码

lodash 中也提供了类似方法,下面是相关源码:

  • findLast()
import findLastIndex from './findLastIndex.js'
import isArrayLike from './isArrayLike.js'
/**
 * This method is like `find` except that it iterates over elements of
 * `collection` from right to left.
 *
 * @since 2.0.0
 * @category Collection
 * @param {Array|Object} collection The collection to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} [fromIndex=collection.length-1] The index to search from.
 * @returns {*} Returns the matched element, else `undefined`.
 * @see find, findIndex, findKey, findLastIndex, findLastKey
 * @example
 *
 * findLast([1, 2, 3, 4], n => n % 2 == 1)
 * // => 3
 */
function findLast(collection, predicate, fromIndex) {
  let iteratee
  const iterable = Object(collection)
  if (!isArrayLike(collection)) {
    collection = Object.keys(collection)
    iteratee = predicate
    predicate = (key) => iteratee(iterable[key], key, iterable)
  }
  const index = findLastIndex(collection, predicate, fromIndex)
  return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined
}
export default findLast
  • findLastIndex()
import baseFindIndex from './.internal/baseFindIndex.js'
import toInteger from './toInteger.js'
/**
 * This method is like `findIndex` except that it iterates over elements
 * of `collection` from right to left.
 *
 * @since 2.0.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} [fromIndex=array.length-1] The index to search from.
 * @returns {number} Returns the index of the found element, else `-1`.
 * @see find, findIndex, findKey, findLast, findLastKey
 * @example
 *
 * const users = [
 *   { 'user': 'barney',  'active': true },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': false }
 * ]
 *
 * findLastIndex(users, ({ user }) => user == 'pebbles')
 * // => 2
 */
function findLastIndex(array, predicate, fromIndex) {
  const length = array == null ? 0 : array.length
  if (!length) {
    return -1
  }
  let index = length - 1
  if (fromIndex !== undefined) {
    index = toInteger(fromIndex)
    index = fromIndex < 0
      ? Math.max(length + index, 0)
      : Math.min(index, length - 1)
  }
  return baseFindIndex(array, predicate, index, true)
}
export default findLastIndex

2. Hashbang Grammar

Unix 的命令行脚本都支持#!命令,又称为 Hashbang。这个命令放在脚本的第一行,用来指定脚本的执行器。Hashbang Grammar 功能就是想为 JavaScript 脚本引入了#!命令,这个命令写在脚本文件或者模块文件的第一行:

typescript

复制代码

// 写在脚本文件的第一行
#!/usr/bin/env node
'use strict';
console.log(1);
// 写在模块文件的第一行
#!/usr/bin/env node
export {};
console.log(1);

这样,Unix 命令行就可以直接执行脚本了:

# 以前执行脚本
node hello.js
# 有了 hashbang 之后执行脚本
./hello.js

不过这样的话,hashbang 就必须严格的在文件头,否则就会出现语法错误,导致这个 JavaScript 脚本文件无法使用。

3. 通过副本更改数组

通过副本更改数组的方法有四个:

  • Array.prototype.toReversed()
  • Array.prototype.toSorted()
  • Array.prototype.toSpliced()
  • Array.prototype.with()

我们知道,大多数的数组方法都是非破坏性的,也就是说,在数组执行该方法时,不会改变原数组,比如 filter() 方法:

javascript

复制代码

const arr = ['a', 'b', 'b', 'a'];
const result = arr.filter(x => x !== 'b');
console.log(result); // ['a', 'a']

当然,也有一些是破坏性的方法,它们在执行时会改变原数组,比如 sort() 方法:

javascript

复制代码

const arr = ['c', 'a', 'b'];
const result = arr.sort();
console.log(result); // ['a', 'b', 'c']

在数组的方法中,下面的方法是具有破坏性的:

  • .reverse()
  • .sort()
  • .splice()

如果我们想要这些数组方法应用于数组而不改变它,可以使用下面任意一种形式:

javascript

复制代码

const sorted1 = arr.slice().sort();
const sorted2 = [...arr].sort();
const sorted3 = Array.from(arr).sort();

可以看到,我们首先需要创建数组的副本,再对这个副本进行修改。因此就引入了这三个方法的非破坏性版本,因此不需要手动创建副本再进行操作:

  • .reverse() 的非破坏性版本:.toReversed()
  • .sort() 非破坏性版本:.toSorted(compareFn)
  • .splice() 非破坏性版本:.toSpliced(start, deleteCount, ...items)

这些函数属性引入到了 Array.prototype

  • Array.prototype.toReversed() -> Array
  • Array.prototype.toSorted(compareFn) -> Array
  • Array.prototype.toSpliced(start, deleteCount, ...items) -> Array
  • Array.prototype.with(index, value) -> Array

除此之外,还有了一个新的非破坏性方法:with()。该方法会以非破坏性的方式替换给定 index 处的数组元素,即 arr[index]=value 的非破坏性版本。

所有这些方法都将保持目标数组不变,并返回它的副本并执行更改。这些方法适用于数组,也适用于类型化数组,即以下类的实例:

  • Int8Array
  • Uint8Array
  • Uint8ClampedArray
  • Int16Array
  • Uint16Array
  • Int32Array
  • Uint32Array
  • Float32Array
  • Float64Array
  • BigInt64Array
  • BigUint64Array

TypedArray是一种通用的固定长度缓冲区类型,允许读取缓冲区中的二进制数据。其在WEBGL规范中被引入用于解决Javascript处理二进制数据的问题。类型化数组也是数组,只不过其元素被设置为特定类型的值。

类型化数组的核心就是一个名为 ArrayBuffer 的类型。每个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer能做的就是为了将来使用而分配一定数量的字节。

这些方法也适用于元组,元组相当于不可变的数组。它们拥有数组的所有方法——除了破坏性的方法。因此,将后者的非破坏性版本添加到数组对元组是有帮助的,这意味着我们可以使用相同的方法来非破坏性地更改数组和元组。

(1)Array.prototype.toReversed()

.toReversed().reverse() 方法的非破坏性版本:

javascript

复制代码

const arr = ['a', 'b', 'c'];
const result = arr.toReversed();
console.log(result); // ['c', 'b', 'a']console.log(arr);    // ['a', 'b', 'c']

下面是 .toReversed() 方法的一个简单的 polyfill:

javascript

复制代码

if (!Array.prototype.toReversed) {
  Array.prototype.toReversed = function () {    returnthis.slice().reverse();  };}

(2)Array.prototype.toSorted()

.toSorted().sort() 方法的非破坏性版本:

javascript

复制代码

const arr = ['c', 'a', 'b'];
const result = arr.toSorted();
console.log(result);  // ['a', 'b', 'c']console.log(arr);     // ['c', 'a', 'b']

下面是 .toSorted() 方法的一个简单的 polyfill:

javascript

复制代码

if (!Array.prototype.toSorted) {
  Array.prototype.toSorted = function (compareFn) {    returnthis.slice().sort(compareFn);  };}

(3)Array.prototype.toSpliced()

.splice() 方法比其他几种方法都复杂,其使用形式:splice(start, deleteCount, ...items)。该方法会从从 start 索引处开始删除 deleteCount个元素,然后在 start 索引处开始插入item 中的元素,最后返回已经删除的元素。

.toSpliced.splice() 方法的非破坏性版本,它会返回更新后的数组,原数组不会变化,并且我们无法再得到已经删除的元素:

javascript

复制代码

const arr = ['a', 'b', 'c', 'd'];
const result = arr.toSpliced(1, 2, 'X');
console.log(result); // ['a', 'X', 'd']console.log(arr);    // ['a', 'b', 'c', 'd']

下面是 .toSpliced() 方法的一个简单的 polyfill:

javascript

复制代码

if (!Array.prototype.toSpliced) {
  Array.prototype.toSpliced = function (start, deleteCount, ...items) {    const copy = this.slice();    copy.splice(start, deleteCount, ...items);    return copy;  };}

(4)Array.prototype.with()

.with()方法的使用形式:.with(index, value),它是 arr[index] = value 的非破坏性版本。

javascript

复制代码

const arr = ['a', 'b', 'c'];
const result = arr.with(1, 'X');
console.log(result);  // ['a', 'X', 'c']console.log(arr);     // ['a', 'b', 'c']

下面是 .with() 方法的一个简单的 polyfill:

javascript

复制代码

if (!Array.prototype.with) {
  Array.prototype.with = function (index, value) {    const copy = this.slice();    copy[index] = value;    return copy;  };}

4. Symbol 作为 WeakMap 的键

目前,WeakMaps 仅允许使用对象作为键,这是 WeakMaps 的一个限制。新功能扩展了 WeakMap API,允许使用唯一的 Symbol 作为键。

这样更易于创建和共享 key:

javascript

复制代码

const weak = newWeakMap();

// 更具象征意义的keyconst key = Symbol('my ref');
const someObject = { /* data data data */ };

weak.set(key, someObject);

除此之外,该功能还解决了记录和元组提案中引入的问题:**如何在原始数据类型中引用和访问非原始值?**Records & Tuples 不能包含对象、函数或方法,当这样做时会抛出 TypeError:

typescript

复制代码

const server = #{
    port: 8080,    handler: function (req) { /* ... */ }, // TypeError!};

这种限制存在是因为记录和元组提案的关键目标之一是默认具有深度不可变性保证和结构相等性。接受 Symbol 值作为 WeakMap 键将允许 JavaScript 库实现它们自己的类似 RefCollection 的东西,它可以重用同时不会随着时间的推移泄漏内存:

class RefBookkeeper {
    #references = new WeakMap();
    ref(obj) {
        const sym = Symbol();
        this.#references.set(sym, obj);
        return sym;
    }
    deref(sym) { return this.#references.get(sym); }
}
globalThis.refs = new RefBookkeeper();
const server = #{
    port: 8080,
    handler: refs.ref(function handler(req) { /* ... */ }),
};
refs.deref(server.handler)({ /* ... */ });
相关文章
|
数据采集 移动开发 文字识别
服务阿里 9 个APP|揭秘新奥创升级的质量演变
新奥创技术体系,是手机淘宝端搭载着星环中台的整个商业化研发体系,孵化出的面对无线电商领域的技术体系。过去一年在手淘完成了下单、详情、购物车三大业务域的改造,接下来还会在订单、手淘导购等领域进行技术升级。目前新奥创已经接入阿里内的9个 App,逐步成为阿里集团无线领域电商系的技术解决方案。 本文主要围绕新奥创技术体系的升级,剖析架构升级对测试保障带来的新的转变,也是新的机遇。
6345 0
服务阿里 9 个APP|揭秘新奥创升级的质量演变
|
2月前
|
人工智能 文字识别 自然语言处理
祁木CAD Translator 原理拆解:基于百炼云技术的DWG/DXF工程图纸结构化翻译技术解析
本文解析DWG/DXF图纸结构化翻译技术,突破OCR识别不准、插件兼容差、人工效率低、通用AI误译等瓶颈;首创三层解耦架构,实现文本精准提取、术语智能保护、动态重排回填、多模式译文写入及本地离线私有化部署,支持中俄/中英/中西/中越等多语种批量高保真翻译,赋能设计院、工程外贸与涉密项目高效合规交付。
471 3
|
2月前
|
人工智能 自然语言处理 语音技术
阿里云千问大模型是什么?模型优势、应用场景、典型模型及其能力介绍
阿里云千问大模型是其自主研发的通义大模型家族核心系列,经万亿级数据训练,形成覆盖文本、图像、音频、视频的全模态产品矩阵。从旗舰级Qwen3.7-Max到轻量化Qwen3.6-Flash,再到视觉理解、图像生成、视频生成、全模态交互等专用模型,可适配复杂推理、高并发响应、长视频解析等多元场景。依托阿里云百炼平台,企业可快速完成模型调用、微调与专属智能体搭建,且作为国内唯一开源自研大模型的云厂商,其产品已首批通过国家大模型标准评测,在编程、办公自动化、多模态内容创作等领域落地广泛。
|
2月前
|
人工智能 监控 Java
全栈测开岗位需求暴增340%:2026秋招最硬核的“版本答案"
2026年测试行业正经历颠覆性变革:AI已覆盖80%回归测试,手工测试岗需求骤降47%。传统“黑盒测试”能力贬值,全栈测开(懂后端+AI+Agent)成新刚需,薪资溢价30%-50%。核心转变是从“验证功能”到“验证AI系统能力”,需掌握Skill封装、MCP协议与Agent验证体系。
|
3月前
|
人工智能 运维 安全
智谱旗舰级大模型GLM 5.2深度实战:API快速集成、MIT权重配置与百万Token上下文能力验证实测
GLM 5.2是智谱AI于2026年6月推出的旗舰级大模型,凭借744B总参数的MoE混合专家架构、原生支持100万Token稳定上下文窗口,以及MIT许可开源权重,成为当前开源模型领域的标杆产品。该模型定位为专注文本与代码的长程任务专家,单次推理仅激活约40B参数,兼顾性能与效率,在长程编程、文档分析、多轮复杂推理等场景表现突出。
1010 0
|
3月前
|
人工智能 运维 Linux
AI Agent进阶:从OpenClaw迁移至Hermes Agent全教程:安装、配置与实战及避坑指南
在AI智能体快速迭代的2026年,各类开源Agent框架逐步从基础工具调用向自进化、长效运行方向升级。OpenClaw作为早期热门AI智能体,凭借稳定的网关架构、丰富的插件生态收获大量用户,而同生态的**Hermes Agent**凭借独有的自进化学习闭环、分层记忆系统、更高的运行透明度,成为新一代优选框架。不少原有OpenClaw用户希望平滑迁移至Hermes Agent,同时保留历史配置、记忆内容与自定义技能。本文结合官方文档与实测经验,全面讲解两款框架的核心架构差异、前置调研要点、完整安装步骤、一键迁移流程、配置调试、多场景实战以及常见问题排查,全程兼顾零基础使用者与专业开发者,帮助用户
440 0
|
存储 Ubuntu 算法
KNIME学习记录
KNIME学习记录
1349 0
|
开发工具 git
git 出现502错误后用depth一步一步来
公司有个项目的git仓库,因为一些二进制文件也放在里面,版本迭代后,整个仓库特别大,有好几G。 直接git clone是不行的,会报这样的错误: error: RPC failed; HTTP 502 curl 2...
3612 0
|
消息中间件 存储 MySQL
消息中间件MetaQ高性能原因分析
## 备注 该篇是由原作者 傅冲 提供 ## 简介 `MetaQ`是一款高性能的消息中间件,经过几年的发展,已经非常成熟稳定,历经多年双11的零点峰值压测,表现堪称完美。 `MetaQ`当前最新最稳定的稳本是`3.x`系统,`MetaQ 3.x`重新设计和实现,比之前的版本更优秀。
7791 99
|
机器学习/深度学习 人工智能 固态存储
YOLO v1详细解读
《You Only Look Once: Unifified, Real-Time Object Detection》 Joseph Redmon∗* , Santosh Divvala∗†, Ross Girshick*¶* , Ali Farhadi∗† University of Washington∗ , Allen Institute for AI*†* , Facebook AI Research*¶**
YOLO v1详细解读

热门文章

最新文章