百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

2025 年每个 JavaScript 开发者都应该了解的功能

myzbx 2025-06-30 18:48 5 浏览

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。

1.Iterator helpers

开发者经常会对同一个数组执行多次链式转换,例如:

const arr = [];
arr
  .slice(10, 20)
  .filter((el) => el < 10)
  .map((el) => el + 5);

这种方式非常低效,因为每次转换都需要分配一个新数组。JS 引入了迭代器方法,其工作原理与常规数组转换类似。不同之处在于其不会创建临时数组,而是创建新的迭代器,这些迭代器会在其他迭代器上进行迭代。

  • Iterator.prototype.drop():返回一个新的 Iterator Helper 对象,该对象会跳过此迭代器开头指定数量的元素,其作用大致与常规数组的 Array.prototype.slice(n) 相同
  • Iterator.prototype.take():返回一个新的 Iterator Helper 对象,该对象从此迭代器的开头获取指定数量的元素,其作用与常规数组的 Array.prototype.slice(0, n) 相同
  • Iterator.prototype.some():类似于 Array.prototype.some(),其测试迭代器生成的元素是否至少有一个通过了所提供函数实现的测试元素
  • Iterator.prototype.every():类似于 Array.prototype.every(),其测试迭代器生成的所有元素是否都通过了开发者所提供函数的测试
  • Iterator.prototype.filter():类似于 Array.prototype.filter(),其返回一个基于筛选值的迭代器
  • Iterator.prototype.find():类似于 Array.prototype.find(),其返回迭代器生成的第一个满足所提供测试函数的元素,否则返回 undefined
function* fibonacci() {
  let current = 1;
  let next = 1;
  while (true) {
    yield current;
    [current, next] = [next, current + next];
  }
}
const isEven = (x) => x % 2 === 0;
console.log(fibonacci().find(isEven));
// 输出 2
const isNegative = (x) => x < 0;
console.log(fibonacci().take(10).find(isNegative));
// undefined
console.log(fibonacci().find(isNegative));
// Never completes
  • Iterator.prototype.flatMap():类似于 Array.prototype.flatMap(),其返回一个基于扁平化值的迭代器
// 展平可迭代对象
const map1 = new Map([
  ["a", 1],
  ["b", 2],
  ["c", 3],
]);
const map2 = new Map([
  ["d", 4],
  ["e", 5],
  ["f", 6],
]);
const merged = new Map([map1, map2].values().flatMap((x) => x));
console.log(merged.get("a"));
// 1
console.log(merged.get("e"));
// 5

该方法可以避免创建任何 map 内容的临时副本。但需要注意的是,数组 [map1, map2] 必须先通过. values() 转换为迭代器,因为 Array.prototype.flatMap() 只能展平数组,而不能展平可迭代对象。

  • Iterator.prototype.forEach():类似于 Array.prototype.forEach(),其对迭代器生成的每个元素执行一次提供的函数
  • Iterator.prototype.map():类似于 Array.prototype.map(),其返回一个由映射函数转换后的值的迭代器
  • Iterator.prototype.reduce():类似于 Array.prototype.reduce(),其对迭代器生成的每个元素执行用户提供的 reducer 回调函数,并传入前一个元素计算的返回值
// 该示例创建迭代器,该迭代器产生斐波那契数列中的项,然后对前十个项求和
function* fibonacci() {
  let current = 1;
  let next = 1;
  while (true) {
    yield current;
    [current, next] = [next, current + next];
  }
}
console.log(
  fibonacci()
    .take(10)
    .reduce((a, b) => a + b),
);
// 输出 143
  • Iterator.prototype.toArray():使用填充的生成值创建一个数组

创建可迭代对象的常用方法是通过静态方法 Iterator.from() 以及 Array、NodeList、Set 和许多其他容器的 values() 方法。因此,上面转换链示例的更节省内存的版本是:

const arr = [];
arr
  .values()
  .drop(10)
  .take(10)
  .filter((el) => el < 10)
  .map((el) => el + 5)
  .toArray();

需要注意的是,这是一个相对较新的功能,最后一个开始支持此功能的主流浏览器是 Safari,其从 2025 年 3 月 31 日才开始支持。

2. 数组的 at() 方法

Array.prototype.at() 是访问第 n 个元素的另一种方法,其还支持负索引,即从最后一个元素开始计数。

例如,[10,20,30].at(-1) 将返回 30,[10,20,30].at(-2) 将返回 20 等等。这种负索引使得访问最后一个元素变得非常容易。在此之前,开发者则必须编写丑陋的样板代码 arr[arr.length - 1]。

3.Promise.withResolvers()

在异步调用场景,开发者经常会编写如下代码:

let resolve, reject;
const promise = new Promise((resolver, rejector) => {
  resolve = resolver;
  reject = rejector;
});
// 在特定场景下调用 resolve 或者 reject 方法

但是,借助于最新的 Promise.withResolvers(),开发者可以轻松实现同样的功能。

const {promise, resolve, reject} = Promise.withResolvers();

Promise.withResolvers() 静态方法返回一个对象,其中包含一个新的 Promise 对象和两个用于 resolve 或 reject 的函数,对应于传递给 Promise() 构造函数执行器的两个参数。

4.String.prototype.replace() / String.prototype.replaceAll() 回调

这是个老生常谈的问题,很多开发者不知道 String.prototype.replace() 或
String.prototype.replaceAll() 的第二个参数可以传入回调函数而不仅仅是字符串。例如:

let counter = 0;
console.log(
  "NUMBER, NUMBER, NUMBER".replaceAll(
    "NUMBER",
    (match) => match + "=" + ++counter
  )
); // NUMBER=1, NUMBER=2, NUMBER=3

replace() 和 replaceAll() 是一个非常强大的功能,其允许一次性完成多次替换。而且,从性能和内存角度来看都非常高效。

5. 交换变量的最新方法

很多开发者经常使用下面的方式来交换变量:

let a = 1,
  b = 2;
console.log(a, b);
// 当前输出 1, 2
const temp = a;
a = b;
b = temp;
console.log(a, b);
// 交换后的输出 2, 1

现在 JavaScript 引擎提供了更加简单的方式来实现同样的功能:

let a = 1,
  b = 2;
console.log(a, b);
// 交换前 1, 2
[a, b] = [b, a];
console.log(a, b);
// 交换后 2, 1

6. 结构化克隆 structuredClone()

大部分浏览器都已经支持 structuredClone() API,其可以深度复制大多数常规对象从而替换 JSON.stringify() 和 JSON.parse() 进行深度复制的功能。因为后者在很多场景可能并不符合预期:

  • JSON.stringify() 不支持某些值,例如: NaN 或 undefined,这些值会被跳过或转换为 null。同时,对于某些数据类型,例如: BigInt,该方法甚至会抛出异常。
  • JSON.stringify() 无法处理包含循环引用的对象:
const obj = {};
obj.selfReference = obj;
console.log(JSON.stringify(obj));
// 直接抛出异常
  • 对于较大的对象来说,该方法效率不高且浪费大量内存

因此总体来看,开发者应尽可能优先使用 structuredClone(),其还会循环引用对象。

const obj = {};
obj.selReference = obj;
const clonedObj = structuredClone(obj);
console.log(obj === clonedObj);
// 输出 false
console.log(clonedObj.selReference === clonedObj);
// 注意:输出 true

7.Tagged templates

大多数开发者都熟悉模板字面量 "`",但很多人并不知道标记模板,标记模板允许开发者使用函数解析模板字面量。标记函数的第一个参数包含一个字符串值数组,其余参数与表达式相关。当想对插值,甚至整个字符串,进行一些自动转换时,标记模板非常有用。

`string text`

`string text line 1
 string text line 2`

`string text ${expression} string text`

tagFunction`string text ${expression} string text`

例如,下面代码示例会在进行插值时自动转义 HTML 文本:

function escapeHtml(strings, ...arguments) {
  const div = document.createElement("div");
  let output = strings[0];
  for (let i = 0; i < arguments.length; ++i) {
    div.innerText = arguments[i];
    output += div.innerHTML;
    output += strings[i + 1];
  }
  return output;
}
console.log(escapeHtml`<br> ${"<br>"}`);
// 输出结果 <br> <br>

8.WeakMap / WeakSet

除了 Map 和 Set 之外,JavaScript 还支持 WeakMap 和 WeakSet。

WeakMap 和 WeakSet 与 Map 和 Set 类似,不同之处在于其不允许将原始值作为键,并且不提供迭代器。这样做的原因是,当指向某个键的所有引用丢失时,该键以及可能关联的值必须能够从 Map/Set 中释放并被垃圾回收。

const set = new WeakSet();
const map = new WeakMap();
{
  const key1 = new Date();
  const key2 = new Date();
  console.log(set.has(key1));
  // false
  set.add(key1);
  console.log(set.has(key1));
  // true
  console.log(map.get(key2));
  // undefined
  map.set(key2, 10);
  console.log(map.get(key2));
  // 10
}
// 注意:此处丢失了对 key1 和 key2 的引用,因此键和值会将被垃圾收集

因此,如果开发者希望将某事物与某个对象关联而不产生副作用,可以考虑使用 WeakMap 或 WeakSet。

9.Set 操作

最近,JavaScript 增加了对 Set 对象的布尔运算支持。

Set.prototype.difference()

集合差运算:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
console.log(set1.difference(set2));
// 输出 Set(2) {1, 2}

Set.prototype.intersection()

执行集合交运算:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
console.log(set1.intersection(set2));
// 输出 Set(2) {3, 4}

Set.prototype.union()

执行集合并操作:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
console.log(set1.union(set2));
// 输出 Set(6) {1, 2, 3, 4, 5, 6}

Set.prototype.symmetricDifference()

除去两个集合的公共元素:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
console.log(set1.symmetricDifference(set2));
// Set(4) {1, 2, 5, 6}

Set.prototype.isDisjointFrom()

返回一个布尔值,指示该集合与给定集合没有共同元素:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
const set3 = new Set([5, 6]);
console.log(set1.isDisjointFrom(set2));
// 输出 false
console.log(set1.isDisjointFrom(set3));
// 输出 true

Set.prototype.isSubsetOf()

返回一个布尔值,指示该集合的所有元素是否都在给定集合中:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
const set3 = new Set([5, 6]);
console.log(set1.isSubsetOf(set2));
// 输出 false
console.log(set3.isSubsetOf(set2));
// 输出  true

Set.prototype.isSupersetOf()

返回一个布尔值,指示给定集合的所有元素都在此集合中:

const set1 = new Set([1, 2, 3, 4]);
const set2 = new Set([3, 4, 5, 6]);
const set3 = new Set([5, 6]);
console.log(set2.isSupersetOf(set1));
// 输出 false
console.log(set2.isSupersetOf(set3));
// 输出 true

参考资料

https://waspdev.com/articles

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/flatMap

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

https://medium.com/@emre.deniz/es2025-the-evolution-of-javascript-93d9f6fd5f5b

https://blog.bitsrc.io/javascript-iterables-vs-iterators-009162379a15?gi=7c4c7a926564

https://www.youtube.com/watch?v=8p9lPsui7Co

相关推荐

使用 Siemens Teamcenter Digital Reality Viewer 打造逼真的数字孪生

现代产品通常由数百万个部件组成,需要复杂的设计和协作。工业世界在管理复杂性方面面临重大挑战,传统的可视化工具无法渲染这些大型的多CAD组件,从而无法充分利用数字孪生的优势。为了解决这些难题,西门子...

如何在JavaScript中实现数字输入框的范围限制?

在JavaScript语言中,实现数字输入框的范围限制可以通过多种方式实现,最常见的方式是利用JavaScript的事件监听机制,和通过JavaScript的条件判断语句来实现范围限制。以...

2.3.8.J速算24点终极挑战(速算24点题目及答案)

各位数学高手、脑力达人,今天给大家带来一道超烧脑的**24点挑战**!**数字:2、3、8、J(J=11规则很简单:**用加减乘除和括号,把四个数字组合成24!每个数字只能用一次!****看起...

分布式系统进阶二十一之短链接生成原理

前言短链接(ShortURL)是一种通过缩短网页链接长度来方便分享的技术。相比于传统的长链接,短链接更简洁明了,更易于在社交媒体等平台上分享和传播。在本文中,我们将会详细解释短链接的定义、作用及其构...

一、SpringBoo中Mybatis多数据源动态切换

我们以一个实例来详细说明一下如何在SpringBoot中动态切换MyBatis的数据源。一、需求1、用户可以界面新增数据源相关信息,提交后,保存到数据库2、保存后的数据源需要动态生效,并且可以由用户动...

「JS 逆向百例」层层嵌套!某加速商城 RSA 加密

声明本文章中所有内容仅供学习交流,敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!逆向目标目标:某加速商城登录接口主页:a...

Spring Data Jpa 介绍和详细入门案例搭建

1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...

SpringBoot 开发 Web 系统,快速入门指南!

01、背景介绍在之前的文章中,我们简单的介绍了SpringBoot项目的创建过程,了解了SpringBoot开箱即用的特性,本篇文章接着上篇的内容继续介绍SpringBoot用于we...

Nacos3.0重磅来袭!全面拥抱AI,单机及集群模式安装详细教程!

之前和大家分享过JDK17的多版本管理及详细安装过程,然后在项目升级完jdk17后又发现之前的注册和配置中心nacos又用不了,原因是之前的nacos1.3版本的,版本太老了,已经无法适配当前新的JD...

golang语言的魅力精华之玩转通道channel 值得你反复阅读100遍

通道用例大全在阅读本文之前,请先阅读通道一文。那篇文章详细地解释了通道类型和通道值,以及各种通道操作的规则细节。一个Go新手程序员可能需要反复多次阅读那篇文章和当前这篇文章来精通Go通道编程。本文...

2小时快速搭建一个高可用的IM系统

“笔者2019年参加了一次Gopher大会,有幸听探探的架构师分享了他们2019年微服务化的过程。图片来自Pexels本文快速搭建的IM系统也是使用Go语言来快速实现的,这里先和...

分库分表以后,如何管理几万张分片表?

大家好,我是小富~ShardingSphere实现分库分表,如何管理分布在不同数据库实例中的成千上万张分片表?上边的问题是之前有个小伙伴看了我的分库分表的文章,私下咨询我的,看到他的提问我第一感觉就是...

Spring Boot JDBC 与 JdbcTemplate 全面指南(万字保姆级教程)

一、SpringBootJDBC基础1.1JDBC简介与演进JDBC(JavaDatabaseConnectivity)是Java语言中用来规范客户端程序如何访问数据库的应用程序...

Flink SQL 知其所以然(六)| 维表 join 的性能优化之路(上)

废话不多说,咱们先直接上本文的目录和结论,小伙伴可以先看结论快速了解博主期望本文能给小伙伴们带来什么帮助:背景及应用场景介绍:博主期望你能了解到,flinksql提供了轻松访问外部存储的loo...

大数据Hadoop之——Flink Table API 和 SQL(单机Kafka)

一、TableAPI和FlinkSQL是什么TableAPI和SQL集成在同一套API中。这套API的核心概念是Table,用作查询的输入和输出,这套API都是批处理和...