一、语法特性
1. 空值合并运算符(??)
空值合并运算符(**??**)是一个逻辑运算符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
const foo = null ?? "default string";
console.log(foo);
// Expected output: "default string"
const baz = 0 ?? 42;
console.log(baz);
// Expected output: 0
2. 动态引入import()
**import()** 语法(通常被称为动态导入)是一种允许异步和动态地将 ECMAScript 模块加载到一个潜在的非模块环境中的类函数表达式。通过动态导入,我们可以在代码运行时才加载需要的模块,而不是将所有的模块一次性加载,
动态导入方式举例:
async function loadModule() {
try {
const module = await import('/modules/my-module.js')
console.log(module.default)
} catch (error) {
}
}
使用注意点:
- 安全性:动态路径可能引发安全问题,特别是当路径来源不可信时。应确保路径变量经过验证和清理,避免加载不该加载的模块。
- 静态分析挑战:构建工具(如 Webpack)在动态路径下难以进行静态分析,这可能影响代码优化。为解决此问题,可将动态导入限制在可预测的范围内或提前定义路径模式。
- 可读性与维护性:动态路径可能降低代码可读性,使维护困难。为改善这点,建议为动态导入添加注释,明确路径的使用条件,并定期审查和优化代码。
总之,动态导入虽灵活,但需注意安全性、维护性和优化之间的平衡,根据项目需求慎重使用。
3. 逻辑或赋值(||=)
逻辑或赋值(x ||= y)运算仅在 x 为假值时为其赋值。 x ||= y 与 x || (x = y) 等价
const a = { duration: 50, title: "" };
a.duration ||= 10;
console.log(a.duration);
// Expected output: 50
a.title ||= "title is empty.";
console.log(a.title);
// Expected output: "title is empty."
4. Error: cause
为开发者理解而编写的错误消息可能并不适合机器解析,因为它们会受到表达方式改变(rewording),或标点符号改变的影响,破坏已有解析器的正常使用。因此,当从函数中抛出错误时,你可以提供结构化的数据作为错误原因(即 cause 字段)供机器解析,以此替代人类可读的错误消息。
try {
connectToDatabase();
} catch (err) {
throw new Error("Connecting to database failed.", { cause: err });
}
二、字符串特性
1. String.prototype.replaceAll()
**replaceAll()** 方法返回一个新字符串,其中所有匹配 pattern 的部分都被替换为 replacement。pattern 可以是一个字符串或一个 RegExp,replacement 可以是一个字符串或一个在每次匹配时调用的函数。原始字符串保持不变。
语法:
replaceAll(pattern, replacement)
示例:
// 使用replaceAll()
"aabbcc".replaceAll("b",".");// 'aa..cc'
// 使用replace()
"aabbcc".replace(new RegExp('b', 'g'), '.');// 'aa..cc'
replaceAll()比replace()使用更方便
2. String.prototype.matchAll()
和 match 方法相比,matchAll 的返回值不仅包括匹配的内容,还包括匹配的分组(也就是正则表达式中括号括起来的部分)。
语法:
matchAll(regexp)
示例:
const regexp = /t(e)(st(\d?))/g;
const str = "test1test2";
// 使用matchAll
const array = [...str.matchAll(regexp)];
// 使用match
const arrayMatch = [...str.match(regexp)];
console.log(array[0]);
// Array ["test1", "e", "st1", "1"]
console.log(array[1]);
// Array ["test2", "e", "st2", "2"]
console.log(arrayMatch[0]);
// "test1"
console.log(arrayMatch[1]);
// "test2"
3. RegExp.prototype.hasIndices
我们常用正则表达式来处理字符串,正则表达式有很多标志,它决定了正则状态机的行为和输出结果。ES13 新增的 d 标志对应正则实例的 hasIndices 属性,当设置 d 标志时,hasIndices 为 true 。使用 d 标志后,正则表达式的匹配结果将包含每个捕获组捕获子字符串的开始和结束游标。
// 字符串
const str = "today is saturday";
// 正则表达式
const reg = /saturday/d;
console.log(reg.hasIndices); // Output: false
// 匹配结果输出游标
const [start, end] = reg.exec(str).indices[0];
console.log([start, end]); // [9, 17]
三、数组特性
1. Array.prototype.toSorted()
实例的 **toSorted()** 方法是 sort() 方法的复制方法版本。它返回一个新数组,其元素按升序排列。
const values = [1, 10, 21, 2];
const sortedValues1 = values.toSorted((a, b) => a - b);
console.log(sortedValues1); // [1, 2, 10, 21]
console.log(values); // [1, 10, 21, 2]
const sortedValues2 = values.sort((a, b) => a - b);
console.log(sortedValues2); // [1, 2, 10, 21]
console.log(values); // [1, 2, 10, 21]
2. Array.prototype.findLast()
**findLast()** 方法反向迭代数组,并返回满足提供的测试函数的第一个元素的值。如果没有找到对应元素,则返回 undefined。
const array1 = [5, 12, 50, 130, 44];
const found = array1.findLast((element) => element > 45);
console.log(found);// Expected output: 130
const found = array1.find((element) => element > 45);
console.log(found);// Expected output: 50
3. Array.prototype.toReversed()
Array 实例的 **toReversed()** 方法是 reverse() 方法对应的复制版本。它返回一个元素顺序相反的新数组。
const items = [1, 2, 3];
console.log(items); // [1, 2, 3]
const reversedItems1 = items.toReversed();
console.log(reversedItems1); // [3, 2, 1]
console.log(items); // [1, 2, 3]
const reversedItems2 = items.reverse();
console.log(reversedItems2); // [3, 2, 1]
console.log(items); // [3, 2, 1]
4. Array.prototype.toSpliced()
Array 实例的 **toSpliced()** 方法是 splice() 方法的复制版本。它返回一个新数组,并在给定的索引处删除和/或替换了一些元素。
你可以通过使用 toSpliced() 来删除、添加和替换数组中的元素,并生成一个新的数组,这比使用 slice() 和 concat() 更高效。
const months = ["Jan", "Mar", "Apr", "May"];
// 在索引 1 处添加一个元素
const months2 = months.toSpliced(1, 0, "Feb");
console.log(months2); // ["Jan", "Feb", "Mar", "Apr", "May"]
// 从第 2 个索引开始删除两个元素
const months3 = months2.toSpliced(2, 2);
console.log(months3); // ["Jan", "Feb", "May"]
// 在索引 1 处用两个新元素替换一个元素
const months4 = months3.toSpliced(1, 1, "Feb", "Mar");
console.log(months4); // ["Jan", "Feb", "Mar", "May"]
// 原数组不会被修改
console.log(months); // ["Jan", "Mar", "Apr", "May"]
5. Array.prototype.with()
Array 实例的 **with()** 方法是使用方括号表示法修改指定索引值的复制方法版本。它会返回一个新数组,其指定索引处的值会被新值替换。
const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6)); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]
6. Array.fromAsync()
**Array.fromAsync()** 静态方法可以由一个异步可迭代对象、可迭代对象或类数组对象创建一个新的、浅拷贝的 Array 实例。
Array.from 可以看作是一个遍历伪数组对象或可迭代对象并转换成数组的方法:
const arr = [];
for (const v of iterable) {
arr.push(v);
}
// 上面等价与下方
const arr = Array.from(iterable);
Array.from 方法,可以将对一个类似数组或可迭代对象创建一个新的、浅拷贝的数组实例。比如:
Array.from({length: 5}, (v, i) => i);
Array.fromAsync 可以将一个异步迭代器转化成为一个 promise,并且 resolve 出一个新的数组。在 promise resolve 之前,会根据输入的参数创建一个异步迭代器,然后惰性的迭代它,并且把每次 yield 的返回值添加到数组中。
const asyncIterable = (async function* () {
for (let i = 0; i < 5; i++) {
await new Promise((resolve) => setTimeout(resolve, 10 * i));
yield i;
}
})();
Array.fromAsync(asyncIterable).then((array) => console.log(array));
// [0, 1, 2, 3, 4]