ES2022 的特性是聚焦于 提升代码的可读性(如类字段声明、at() 方法)、增强封装性(如私有成员)、优化异步流程(如顶层 await)和错误处理(如 Error Cause)。
一、 类字段声明
在之前的 JavaScript 中,字段需要在 constructor 中通过 this.xxx 声明,代码分散且不直观。新的版本将通过在类体中直接声明实例字段,无需依赖 constructor。
class Dog {
name: 'Lucky';
age;
constructor(age) {
this.age = age || 0;
}
}
const lucky = new Dog(3);
console.log(lucky.name); // "lucky"
console.log(lucky.age); // 3
二、 私有方法和私有访问器
此前缺乏真正的私有成员,通过命名约定(如 _private)模拟的私有属性仍可被外部访问,存在安全隐患。现在通过 # 前缀定义私有字段、私有方法和私有访问器(getter、setter),仅允许在类的内部访问。
class BankAccount {
#balance = 0; // 私有字段
// 私有方法
#validateAmount(amount) {
return amount > 0;
}
// 私有访问器(getter)
get #currentBalance() {
return this.#balance;
}
deposit(amount) {
if (this.#validateAmount(amount)) {
// 类内部可访问私有方法
this.#balance += amount;
}
}
getBalance() {
return this.#currentBalance; // 类内部可访问私有getter
}
}
const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
// 外部访问私有成员会报错
console.log(account.#balance); // SyntaxError
account.#validateAmount(50); // SyntaxError
三、 静态私有成员
静态成员(类级别的成员)同样需要私有性,但此前无法限制外部访问。通过 # 前缀定义静态私有字段和方法,仅允许在类自身内部访问(无法被实例或子类访问)。
class MathUtil {
static #PI = 3.14159; // 静态私有字段
static #calculateArea(radius) {
// 静态私有方法
return this.#PI * radius ** 2;
}
static getCircleArea(radius) {
return this.#calculateArea(radius); // 类内部可访问静态私有成员
}
}
console.log(MathUtil.getCircleArea(2)); // 12.56636
// 外部访问静态私有成员会报错
console.log(MathUtil.#PI); // SyntaxError
MathUtil.#calculateArea(2); // SyntaxError
四、 类静态初始化块
在类中使用 static {} 定义静态初始块,用于执行类级别的初始化逻辑(如加载配置、初始化静态字段)。静态块在类定义是执行,仅执行一次。
class Config {
static apiURL;
static {
// 静态初始化块,加载配置
Config.apiURL = fetch('api/config').then(res => res.json);
}
}
// Promise 对象(静态块已执行,但异步操作未完成)
console.log(Config.apiURL);
五、 顶层 await
await 之前仅允许在 async 函数中使用,模块化初始化时需要异步操作(如加载配置),需嵌套在 async 函数中,代码繁琐。新的版本允许在模块的顶层直接使用 await,暂停模块执行直到异步的完成没,其他依赖该模块的代码需要等待其加载完成。
// config.js(模块)
// 顶层await加载远程配置
export const config = await fetch('/api/config')
.then(res => res.json())
.catch(err => {
console.error('Failed to load config:', err);
return { default: 'config' };
});
// app.js(依赖config.js的模块)
import { config } from './config.js';
// 等待config加载完成后再执行
console.log('Loaded config:', config);
- 顶层
await仅能在 ES 模块 (.mjs 或 package.json 设置"type": "module")中使用,普通脚本(.js)不支持 - 模块的执行顺序会因
await暂停,需确保依赖关系的正确性
六、 at() 方法
访问数组/字符串最后一个元素,之前需要使用 arr[arr.length - 1] 实现,代码冗长。现在可使用 at() 直接使用正负索引访问元素。
const arr = [10, 20, 30];
console.log(arr.at(-1)); // 30
console.log(arr.at(1)); // 10
const str = 'hello';
console.log(str.at(-1)); // 'o'
console.log(str.at(2)); // 'l'
七、 正则表达式匹配索引
正则匹配结果仅返回匹配内容,无法直接获取匹配的字符串在原文中的起始和结束位置,需额外计算。通过正则表达式的 d 标志,使 exec()、match() 等方法返回的结果包含 indices 属性,记录匹配内容及捕获组的起始和结果索引。
const text = 'JavaScript ES2022';
const regex = /ES(\d+)/d; // 带 `d` 标志的正则
const match = regex.exec(text);
// "ES2022" (完整匹配)
console.log(match[0]);
// "2022" (捕获组)
console.log(match[1]);
// [[11, 17], [13, 17], groups: undefined]
console.log(match.indices);
八、 Error Cause (错误原因链)
多层捕获错误时,原始错误信息容易丢失,难以追踪根源。Error 构造函数新增 cause 选项,允许抛出新错误时附加原始错误,形成错误链。
async function fetchData() {
try {
const response = await fetch('/invalid-url');
if (!response.success) throw new Error('Request failed');
} catch (error) {
throw new Error('Failed to fetch data', { cause: error });
}
}
// 捕获上层错误
try {
await fetchData();
} catch (error) {
console.log(error.message);
console.log(error.cause);
}
九、 Object.hasOwn() 方法
检查对象自身是否包含某个属性时,传统方式 Object.prototype.hasOwnProperty.call(obj, prop) 代码冗长易出错。现新增 Object.hasOwn() 静态方法,直接检查对象自身是否有某属性(忽略原型链)。
const obj = { name: 'Tom' };
// 上古方式
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // true
// 新方式
console.log(Object.hasOwn(obj, 'name')); // true
console.log(Object.hasOwn(obj, 'toString')); // false
十、 私有字段的 in 操作符检测优化
可以使用 in 操作符中使用 #fieldName 来检测一个对象是否包含了特定的私有字段,而不会抛出错误