ES6 新特性 从 ECMAScript 2015(ES6)到 ECMAScript 2025
- ECMAScript 2015 (ES6)
- ECMAScript 2016 (ES7)
- ECMAScript 2017 (ES8)
- ECMAScript 2018 (ES9)
- ECMAScript 2019 (ES10)
- ECMAScript 2020 (ES11)
- ECMAScript 2021 (ES12)
- ECMAScript 2022 (ES13)
- ECMAScript 2023 (ES14)
- ECMAScript 2024 (ES15) - 已发布的部分特性
- ECMAScript 2025 (ES16) - 预计的特性
ECMAScript 2015 (ES6) - 2015年6月
let 和 const 变量声明
let 声明的变量具有块级作用域:
js
// let - 块级作用域变量
defineFunction() {
let count = 0;
if (true) {
let count = 1; // 不会覆盖外部作用域的变量
console.log(count); // 输出: 1
}
console.log(count); // 输出: 0
}
// const - 常量声明
const PI = 3.14159;
// PI = 3.14; // 报错: Assignment to constant variable
js
// 示例
if (true) {
let x = 5;
console.log(x); // 5
}
console.log(typeof x); // "undefined",因为x在块外不可访问
// 防止变量提升导致的问题
console.log(y); // undefined (var声明的变量会被提升)
console.log(z); // 报错:Cannot access 'z' before initialization
var y = 10;
let z = 20;
const 声明常量,不可重新赋值:
js
// 示例
const PI = 3.14159;
console.log(PI); // 3.14159
// PI = 3; // 报错:Assignment to constant variable
// 对象和数组的引用是常量,但内容可以修改
const person = { name: "John" };
person.name = "Jane"; // 允许
console.log(person); // { name: "Jane" }
箭头函数 (Arrow Functions)
提供简洁的函数定义语法,不绑定自己的 this :
js
// 普通函数
function add(a, b) {
return a + b;
}
// 箭头函数简化写法
const add = (a, b) => a + b;
// 保留this上下文
const obj = {
name: "Test",
regularFunction: function() {
setTimeout(function() {
console.log(this.name); // 输出: undefined
}, 100);
},
arrowFunction: function() {
setTimeout(() => {
console.log(this.name); // 输出: Test
}, 100);
}
};
js
// 基本语法
const sum = (a, b) => a + b;
console.log(sum(2, 3)); // 5
// 单个参数可以省略括号
const square = x => x * x;
console.log(square(4)); // 16
// 无参数需要括号
const random = () => Math.random();
// 多行函数体需要大括号和return
const greet = name => {
const message = `Hello, ${name}!`;
return message;
};
// 保持上下文的this
const obj = {
name: "Object",
regularFunc: function() {
setTimeout(function() {
console.log(this.name); // undefined (this指向全局)
}, 100);
},
arrowFunc: function() {
setTimeout(() => {
console.log(this.name); // "Object" (this继承自外层)
}, 100);
}
};
模板字符串 (Template Literals)
使用反引号定义字符串,支持多行和变量插值:
js
const name = "John";
const age = 30;
// 传统字符串拼接
const greeting1 = "Hello, " + name + ". You are " + age + " years old.";
// 模板字符串
const greeting2 = `Hello, ${name}. You are ${age} years old.`;
// 多行字符串
const multiLine = `This is a
multi-line
string.`;
js
// 变量插值
const name = "Alice";
const age = 25;
const greeting = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(greeting); // "Hello, my name is Alice and I am 25 years old."
// 多行字符串
const multiLine = `This is line 1
This is line 2
This is line 3`;
// 表达式插值
const a = 10;
const b = 5;
console.log(`${a} + ${b} = ${a + b}`); // "10 + 5 = 15"
解构赋值 (Destructuring Assignment)
从数组或对象中提取值并赋给变量:
js
// 对象解构
const user = { name: "Alice", age: 25, city: "New York" };
const { name, age } = user;
console.log(name, age); // 输出: Alice 25
// 数组解构
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first, second, rest); // 输出: 1 2 [3, 4, 5]
// 函数参数解构
function printUser({ name, age }) {
console.log(`${name} is ${age} years old.`);
}
printUser(user); // 输出: Alice is 25 years old.
js
// 数组解构
const [first, second, third] = [1, 2, 3];
console.log(first); // 1
console.log(second); // 2
// 跳过元素
const [x, , z] = [1, 2, 3];
console.log(x); // 1
console.log(z); // 3
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2, 3, 4]
// 对象解构
const person = { firstName: "John", lastName: "Doe", age: 30 };
const { firstName, age } = person;
console.log(firstName); // "John"
console.log(age); // 30
// 重命名
const { firstName: fName, lastName: lName } = person;
console.log(fName); // "John"
// 默认值
const { country = "Unknown" } = person;
console.log(country); // "Unknown"
默认参数
js
function greet(name = "Guest", greeting = "Hello") {
return `${greeting}, ${name}!`;
}
console.log(greet()); // 输出: Hello, Guest!
console.log(greet("Alice")); // 输出: Hello, Alice!
console.log(greet("Bob", "Hi")); // 输出: Hi, Bob!
类 (Classes)
js
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, my name is ${this.name}.`;
}
static createAdult(name) {
return new Person(name, 18);
}
}
class Employee extends Person {
constructor(name, age, position) {
super(name, age);
this.position = position;
}
getJobInfo() {
return `${this.name} works as ${this.position}.`;
}
}
const alice = new Person("Alice", 25);
console.log(alice.greet()); // 输出: Hello, my name is Alice.
const bob = Employee.createAdult("Bob");
console.log(bob.getJobInfo()); // 输出: Bob works as undefined.
模块系统 (Modules) - import/export
js
// 在 utils.js 中
export const PI = 3.14159;
export function calculateArea(radius) {
return PI * radius * radius;
}
export default function sayHello() {
return "Hello from module!";
}
// 在 main.js 中
import sayHello, { PI, calculateArea } from './utils.js';
console.log(sayHello()); // 输出: Hello from module!
console.log(`Area of circle with radius 5: ${calculateArea(5)}`); // 输出: Area of circle with radius 5: 78.53975
Promise
处理异步操作的对象:
js
// 创建Promise
const fetchData = new Promise
((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("Data fetched successfully");
} else {
reject(new Error("Failed to fetch data"));
}
}, 1000);
});
// 使用Promise
fetchData
.then(data => {
console.log(data); // "Data fetched successfully"
return data.toUpperCase();
})
.then(uppercaseData => {
console.log(uppercaseData); // "DATA FETCHED SUCCESSFULLY"
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("Operation completed");
});
js
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve({ data: "Success!" });
} else {
reject(new Error("Failed to fetch data"));
}
}, 1000);
});
}
// 使用 Promise
fetchData()
.then(result => console.log(result.data))
.catch(error => console.error(error))
.finally(() => console.log("Operation completed"));
Map 和 Set 数据结构
js
// Map
const userMap = new Map();
userMap.set(1, { name: "Alice", age: 25 });
userMap.set(2, { name: "Bob", age: 30 });
console.log(userMap.get(1)); // 输出: { name: "Alice", age: 25 }
console.log(userMap.size); // 输出: 2
// Set
const numbersSet = new Set([1, 2, 3, 4, 4, 5]);
console.log(numbersSet.size); // 输出: 5 (自动去重)
console.log(numbersSet.has(4)); // 输出: true
numbersSet.add(6);
numbersSet.delete(2);
Symbol 原始数据类型
js
// 创建唯一标识符
const id1 = Symbol("id");
const id2 = Symbol("id");
console.log(id1 === id2); // 输出: false
// 作为对象属性键
const user = {
[id1]: "Unique ID",
name: "Alice"
};
// Symbol 不会被常规遍历方法获取
console.log(Object.keys(user)); // 输出: ["name"]
console.log(Object.getOwnPropertySymbols(user)); // 输出: [Symbol(id)]
迭代器和生成器 (Iterators & Generators)
js
// 迭代器
const iterable = {
[Symbol.iterator]() {
let count = 0;
return {
next() {
count++;
if (count <= 5) {
return { value: count, done: false };
} else {
return { done: true };
}
}
};
}
};
// 生成器
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
const generator = generateSequence();
console.log(generator.next().value); // 输出: 1
console.log(generator.next().value); // 输出: 2
console.log(generator.next().value); // 输出: 3
// 生成器函数简化迭代
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
for...of 循环
js
const numbers = [1, 2, 3, 4, 5];
// 遍历数组
for (const number of numbers) {
console.log(number);
}
// 遍历字符串
for (const char of "hello") {
console.log(char);
}
// 遍历 Map
const userMap = new Map([["name", "Alice"], ["age", 25]]);
for (const [key, value] of userMap) {
console.log(`${key}: ${value}`);
}
对象字面量增强
js
const name = "Alice";
const age = 25;
const greet = function() { return "Hello!"; };
// ES6 增强的对象字面量
const user = {
// 简写属性
name,
age,
// 简写方法
greet() {
return "Hello!";
},
// 计算属性名
["prop" + (1 + 2)]: "Computed Property",
// 方法中使用 super
getGreeting() {
return super.toString();
}
};
展开运算符 (Spread Operator)
js
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // [1, 2, 3, 4, 5]
// 合并数组
const arr3 = [...arr1, ...arr2];
console.log(arr3); // [1, 2, 3, 1, 2, 3, 4, 5]
// 复制数组
const arrCopy = [...arr1];
// 展开对象
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 };
console.log(obj2); // { a: 1, b: 2, c: 3 }
// 函数参数展开
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
ECMAScript 2016 (ES7)
Array.prototype.includes()
检查数组是否包含某个元素:
js
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.includes(3)); // 输出: true
console.log(numbers.includes(6)); // 输出: false
// 从指定索引开始搜索
console.log(numbers.includes(3, 3)); // 输出: false (从索引3开始搜索)
js
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.includes(3)); // true
console.log(numbers.includes(6)); // false
// 从指定索引开始搜索
console.log(numbers.includes(3, 3)); // false (从索引3开始查找)
// 处理NaN
const mixed = [1, 2, NaN, 4];
console.log(mixed.includes(NaN)); // true (与indexOf不同)
console.log(mixed.indexOf(NaN)); // -1
指数运算符 (**)
js
// 等同于 Math.pow(2, 3)
console.log(2 ** 3); // 输出: 8
console.log(5 ** 2); // 输出: 25
console.log(10 ** -1); // 输出: 0.1
// 赋值运算符形式
let num = 2;
num **= 3;
console.log(num); // 输出: 8
js
console.log(2 ** 3); // 8 (2的3次方)
console.log(10 ** -1); // 0.1 (10的-1次方)
// 与赋值运算符结合
let x = 2;
x **= 3;
console.log(x); // 8
ECMAScript 2017 (ES8)
Async/Await
异步编程语法糖,使异步代码更易读:
js
function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve("Data fetched"), 1000);
});
}
async function processData() {
try {
console.log("Start processing");
const data = await fetchData();
console.log(data);
console.log("Processing complete");
} catch (error) {
console.error("Error processing data", error);
}
}
processData();
// 输出:
// Start processing
// Data fetched
// Processing complete
js
// 定义异步函数
async function fetchUserData(userId) {
try {
// await等待Promise解决
const response = await fetch(`https://api.example.com/users/${userId}`);
const userData = await response.json();
return userData;
} catch (error) {
console.error("Error fetching user data:", error);
throw error;
}
}
// 使用异步函数
async function displayUser() {
try {
const user = await fetchUserData(1);
console.log(user);
} catch (error) {
console.error("Failed to display user", error);
}
}
displayUser();
Object.values() 和 Object.entries()
js
const user = { name: "Alice", age: 25, city: "New York" };
// 获取所有值
const values = Object.values(user);
console.log(values); // 输出: ["Alice", 25, "New York"]
// 获取所有键值对
const entries = Object.entries(user);
console.log(entries); // 输出: [["name", "Alice"], ["age", 25], ["city", "New York"]]
// 使用 for...of 遍历
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}
js
const person = { name: "John", age: 30, city: "New York" };
// 获取所有值
console.log(Object.values(person)); // ["John", 30, "New York"]
// 获取所有键值对
console.log(Object.entries(person)); // [["name", "John"], ["age", 30], ["city", "New York"]]
// 遍历对象
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
// name: John
// age: 30
// city: New York
String.prototype.padStart() 和 String.prototype.padEnd() 方法
js
const str = "5";
// 用指定字符填充到指定长度
console.log(str.padStart(3, "0")); // 输出: "005"
console.log(str.padEnd(3, "0")); // 输出: "500"
// 不指定填充字符则默认使用空格
console.log("abc".padStart(5)); // 输出: " abc"
ECMAScript 2018 (ES9)
异步迭代器 (Async Iterators)
js
async function* asyncGenerator() {
for (let i = 1; i <= 3; i++) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
async function processAsyncIterator() {
for await (const value of asyncGenerator()) {
console.log(value);
}
}
processAsyncIterator();
// 每隔1秒输出: 1, 2, 3
Promise.prototype.finally()
无论Promise成功或失败都会执行的回调:
js
fetchData()
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => {
// 无论成功或失败都会执行
console.log("Operation completed");
// 可以在这里关闭加载指示器等
});
js
fetchData()
.then(data => console.log(data))
.catch(error => console.error
(error))
.finally(() => {
console.log("Operation completed");
// 这里可以执行清理操作,如隐藏加载指示器
});
Rest/Spread 属性 用于对象
js
// Rest 属性
const user = { name: "Alice", age: 25, city: "New York", country: "USA" };
const { name, age, ...address } = user;
console.log(name); // 输出: Alice
console.log(age); // 输出: 25
console.log(address); // 输出: { city: "New York", country: "USA" }
// Spread 属性
const defaults = { theme: "light", language: "en" };
const settings = { notifications: true, ...defaults };
console.log(settings); // 输出: { notifications: true, theme: "light", language: "en" }
正则表达式命名捕获组
js
// 传统捕获组
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const match = regex.exec("2023-10-15");
console.log(match[1]); // "2023"
console.log(match[2]); // "10"
console.log(match[3]); // "15"
// 命名捕获组
const namedRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const namedMatch = namedRegex.exec("2023-10-15");
console.log(namedMatch.groups.year); // "2023"
console.log(namedMatch.groups.month); // "10"
console.log(namedMatch.groups.day); // "15"
// 在replace中使用
const dateStr = "2023-10-15";
const formattedDate = dateStr.
replace(namedRegex, "$<day>/$<month>/$<year>");
console.log(formattedDate); // "15/10/2023"
ECMAScript 2019 (ES10)
Array.prototype.flat() 和 Array.prototype.flatMap()
js
// flat() - 展平数组
const nestedArray = [1, [2, 3], [4, [5, 6]]];
console.log(nestedArray.flat()); // 输出: [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(2)); // 输出: [1, 2, 3, 4, 5, 6]
// flatMap() - 映射后展平
const numbers = [1, 2, 3];
const result = numbers.flatMap(n => [n, n * 2]);
console.log(result); // 输出: [1, 2, 2, 4, 3, 6]
js
// 展平数组
const nestedArray = [1, [2, 3], [4, [5, 6]]];
console.log(nestedArray.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(2)); // [1, 2, 3, 4, 5, 6] (指定深度)
console.log(nestedArray.flat(Infinity)); // 展平所有层级
// flatMap结合map和flat
const numbers = [1, 2, 3, 4];
const result = numbers.flatMap(n => [n, n * 2]);
console.log(result); // [1, 2, 2, 4, 3, 6, 4, 8]
String.prototype.trimStart() 和 String.prototype.trimEnd() 方法
js
const str = " Hello World! ";
console.log(str.trimStart()); // 输出: "Hello World! "
console.log(str.trimEnd()); // 输出: " Hello World!"
console.log(str.trim()); // 输出: "Hello World!"
Object.fromEntries()
从键值对数组创建对象:
js
// 从键值对数组创建对象
const entries = [["name", "Alice"], ["age", 25], ["city", "New York"]];
const user = Object.fromEntries(entries);
console.log(user); // 输出: { name: "Alice", age: 25, city: "New York" }
// 转换 Map 为对象
const map = new Map(entries);
const obj = Object.fromEntries(map);
console.log(obj); // 输出: { name: "Alice", age: 25, city: "New York" }
js
const entries = [['name', 'John'], ['age', 30], ['city', 'New York']];
const person = Object.fromEntries(entries);
console.log(person); // { name: 'John', age: 30, city: 'New York' }
// 实际应用:对象转换
const obj = { a: 1, b: 2, c: 3 };
const transformed = Object.fromEntries(
Object.entries(obj)
.map(([key, value]) => [key,
value * 2])
);
console.log(transformed); // { a: 2, b: 4, c: 6 }
ECMAScript 2020 (ES11)
可选链操作符 (?. )
安全地访问嵌套对象属性:
js
const user = {
name: "Alice",
address: {
city: "New York",
coordinates: { lat: 40.7128, lng: -74.006 }
}
};
// 安全访问嵌套属性
console.log(user?.address?.city); // 输出: "New York"
console.log(user?.contact?.email); // 输出: undefined (不会抛出错误)
// 安全调用方法
console.log(user.sayHello?.()); // 输出: undefined
// 安全访问数组索引
console.log(user.addresses?.[0]); // 输出: undefined
js
const user = {
profile: {
name: "John",
address: {
city: "New York"
}
}
};
// 传统方式需要检查每个层级
const city = user && user.profile && user.profile.address && user.profile.address.city;
// 使用可选链
const cityOptional = user?.profile?.address?.city;
console.log(cityOptional); // "New York"
// 处理不存在的属性
const country = user?.profile?.address?.country;
console.log(country); // undefined (不会报错)
// 可选链与函数调用
const greet = user?.greet?.(); // 如果greet存在则调用,否则返回undefined
空值合并运算符 (?? )
当左侧操作数为null或undefined时,返回右侧操作数:
js
// 只有当左侧为 null 或 undefined 时才返回右侧值
const value1 = null ?? "Default";
console.log(value1); // 输出: "Default"
const value2 = undefined ?? "Default";
console.log(value2); // 输出: "Default"
const value3 = "" ?? "Default";
console.log(value3); // 输出: "" (空字符串不触发默认值)
const value4 = 0 ?? "Default";
console.log(value4); // 输出: 0 (0 不触发默认值)
js
// 传统逻辑或的问题
const count = 0;
console.log(count || "Not specified"); // "Not specified" (0被视为false)
// 使用空值合并运算符
console.log(count ?? "Not specified"); // 0 (只有null或undefined才返回右侧)
const name = null;
console.log(name ?? "Unknown"); // "Unknown"
const city = undefined;
console.log(city ?? "Default City"); // "Default City"
// 结合可选链
const userCity = user?.profile?.address?.city ?? "Unknown City";
BigInt
处理任意精度整数:
js
// 创建 BigInt
const bigNumber = BigInt(9007199254740991); // 最大安全整数
const biggerNumber = 9007199254740991n; // 直接使用 n 后缀
console.log(bigNumber + 1n); // 输出: 9007199254740992n
console.log(biggerNumber * 2n); // 输出: 18014398509481982n
// 注意: BigInt 和 Number 不能直接混合运算
// console.log(bigNumber + 1); // 报错
console.log(bigNumber + BigInt(1)); // 正常工作
js
// 创建BigInt
const bigNum1 = BigInt(123456789012345678901234567890);
const bigNum2 = 123456789012345678901234567890n;
// 操作
const sum = bigNum1 + bigNum2;
console.log(sum); // 246913578024691357802469135780n
// 注意:不能直接与普通数字混合运算
// const mixed = bigNum1 + 1; // 报错
const mixed = bigNum1 + BigInt(1); // 正确
// 比较
console.log(BigInt(10) === 10n); // true
console.log(BigInt(10) == 10); // true
console.log(BigInt(10) > 9); // true
ECMAScript 2021 (ES12)
String.prototype.replaceAll() 方法
js
const str = "Hello, world! Hello, JavaScript!";
const newStr = str.replaceAll("Hello", "Hi");
console.log(newStr); // 输出: "Hi, world! Hi, JavaScript!"
// 与正则表达式一起使用
const regexStr = str.replaceAll(/Hello/g, "Hey");
console.log(regexStr); // 输出: "Hey, world! Hey, JavaScript!"
逻辑赋值运算符
结合逻辑运算符和赋值:
js
// &&= - 只有当左侧为真时才赋值
let x = 1;
x &&= 2; // x 现在是 2
let y = 0;
y &&= 2; // y 保持 0
// ||= - 只有当左侧为假时才赋值
let a = 0;
a ||= 5; // a 现在是 5
let b = 10;
b ||= 5; // b 保持 10
// ??= - 只有当左侧为 null 或 undefined 时才赋值
let c = null;
c ??= 100; // c 现在是 100
let d = "";
d ??= 100; // d 保持 ""
js
// 逻辑与赋值 (&&=)
let a = 1;
let b = 2;
a &&= b;
console.log(a); // 2 (相当于 a = a && b)
// 逻辑或赋值 (||=)
let c = null;
let d = "default";
c ||= d;
console.log(c); // "default" (相当于 c = c || d)
// 空值合并赋值 (??=)
let e;
let f = "fallback";
e ??= f;
console.log(e); // "fallback" (相当于 e = e ?? f)
数字分隔符
提高大数字的可读性:
js
// 提高大数字的可读性
const billion = 1_000_000_000;
console.log(billion); // 输出: 1000000000
const hexValue = 0xA0_B0_C0;
console.log(hexValue); // 输出: 10514496
const binaryValue = 0b1010_1011;
console.log(binaryValue); // 输出: 171
js
const billion = 1_000_000_000;
console.log(billion); // 1000000000
const hexColor = 0xFF_FF_FF; // 白色
console.log(hexColor); // 16777215
const binary = 0b1010_1011;
console.log(binary); // 171
ECMAScript 2022 (ES13)
顶层 await Top-level await 支持
在模块顶层使用await,无需包装在async函数中:
js
// 在模块顶层直接使用 await
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log('Data loaded:', data);
export default data;
js
// 以前的方式
(async function() {
const data = await fetchData();
console.log(data);
})();
// 现在可以直接在模块顶层使用
const data = await fetchData();
console.log(data);
// 实际应用:动态导入
const feature = await import('./feature.js');
feature.initialize();
类字段声明
直接在类中声明实例字段和静态字段:
js
class Person {
// 公共实例字段
name = "Guest";
age = 0;
// 私有实例字段
#id = Math.random();
// 静态公共字段
static species = "Homo sapiens";
// 静态私有字段
static #count = 0;
constructor(name, age) {
this.name = name;
this.age = age;
Person.#count++;
}
// 私有方法
#generateId() {
return this.#id;
}
// 私有 getter
get #personId() {
return this.#id;
}
// 静态方法访问静态私有字段
static getCount() {
return Person.#count;
}
}
const alice = new Person("Alice", 25);
console.log(alice.name); // 输出: Alice
console.log(Person.species); // 输出: Homo sapiens
console.log(Person.getCount()); // 输出: 1
// console.log(alice.#id); // 报错: Private field '#id' must be declared in an enclosing class
js
class Person {
// 公共实例字段
name = "Anonymous";
age = 0;
// 私有实例字段 (以#开头)
#email;
// 静态公共字段
static species = "Homo sapiens";
// 静态私有字段
static #count = 0;
constructor(name, age, email) {
this.name = name;
this.age = age;
this.#email = email;
Person.#count++;
}
// 私有方法
#validateEmail() {
return this.#email.includes('@');
}
getEmail() {
if (this.#validateEmail()) {
return this.#email;
}
return "Invalid email";
}
static getCount() {
return Person.#count;
}
}
const john = new Person("John", 30, "john@example.com");
console.log(john.name); // "John"
console.log(john.getEmail()); // "john@example.com"
console.log(Person.species); // "Homo sapiens"
console.log(Person.getCount()); // 1
// console.log(john.#email); // 报错:私有字段不可访问
ECMAScript 2023 (ES14)
数组复制方法 (Change Array by Copy)
js
const arr = [1, 2, 3, 4, 5];
// toSorted - 返回排序后的新数组
const sorted = arr.toSorted();
console.log(sorted); // 输出: [1, 2, 3, 4, 5]
console.log(arr); // 原数组不变: [1, 2, 3, 4, 5]
// toReversed - 返回反转后的新数组
const reversed = arr.toReversed();
console.log(reversed); // 输出: [5, 4, 3, 2, 1]
// toSpliced - 返回删除/替换/添加元素后的新数组
const spliced = arr.toSpliced(2, 2, 99, 100);
console.log(spliced); // 输出: [1, 2, 99, 100, 5]
// with - 返回修改指定索引后的新数组
const withItem = arr.with(2, 999);
console.log(withItem); // 输出: [1, 2, 999, 4, 5]
Array.prototype.toSorted()、toReversed()、toSpliced()、with()
这些方法返回新数组,不修改原数组:
js
const numbers = [3, 1, 4, 1, 5, 9];
// toSorted - 返回排序后的新数组
const sorted = numbers.toSorted();
console.log(sorted); // [1, 1, 3, 4, 5, 9]
console.log(numbers); // [3, 1, 4,1, 5, 9] (原数组不变)
// toReversed - 返回反转后的新数组
const reversed = numbers.toReversed();
console.log(reversed); // [9, 5, 1,4, 1, 3]
// toSpliced - 类似于splice但返回新数组
const spliced = numbers.toSpliced(2, 2, 10, 11);
console.log(spliced); // [3, 1, 10, 11, 5, 9]
// with - 替换指定索引的元素并返回新数组
const replaced = numbers.with(2, 100);
console.log(replaced); // [3, 1, 100, 1, 5, 9]
Object.hasOwn()
检查对象自身是否有指定属性(更安全的hasOwnProperty替代):
js
const person = { name: "John" };
console.log(Object.hasOwn(person, "name")); // true
console.log(Object.hasOwn(person, "toString")); // false (继承的属性)
// 处理对象没有hasOwnProperty方法的情况
const obj = Object.create(null); // 创建没有原型的对象
obj.prop = "value";
console.log(Object.hasOwn(obj, "prop")); // true
// console.log(obj.hasOwnProperty("prop")); // 报错:obj.hasOwnProperty is not a function
ECMAScript 2024 (ES15)
Array.fromAsync()
从异步可迭代对象创建数组:
js
// 从异步可迭代对象创建数组
async function example() {
const asyncIterable = {
async *[Symbol.asyncIterator]() {
yield 1;
await new Promise(r => setTimeout(r, 100));
yield 2;
await new Promise(r => setTimeout(r, 100));
yield 3;
}
};
const arr = await Array.fromAsync(asyncIterable);
console.log(arr); // 输出: [1, 2, 3]
}
example();
js
// 示例:从异步生成器创建数组
async function* asyncGenerator() {
yield 1;
await new Promise(resolve =>
setTimeout(resolve, 100));
yield 2;
yield 3;
}
async function example() {
const array = await Array.
fromAsync(asyncGenerator());
console.log(array); // [1, 2, 3]
}
example();
// 示例:处理Fetch API响应
async function processFetchResponse
() {
const response = await fetch('https://api.example.com/data');
const data = await Array.fromAsync
(
response.body.pipeThrough(new TextDecoderStream())
);
console.log(data); // 响应数据的数组形式
}
Promise.withResolvers()
简化Promise创建:
js
// 简化 Promise 创建
function timeout(ms) {
const { promise, resolve, reject } = Promise.withResolvers();
const id = setTimeout(() => {
resolve(`Timed out after ${ms}ms`);
}, ms);
// 可以在此处存储 resolve 和 reject 引用供后续使用
return promise;
}
timeout(1000).then(console.log); // 输出: Timed out after 1000ms
js
// 传统方式
function createPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
// 使用withResolvers
function createBetterPromise() {
return Promise.withResolvers();
}
// 使用示例
const { promise, resolve, reject } = Promise.withResolvers();
promise.then(value => {
console.log('Resolved with:', value);
}).catch(error => {
console.error('Rejected with:', error);
});
// 稍后解析
setTimeout(() => {
resolve('Success!');
}, 1000);
ECMAScript 2025 (ES16) 提案特性
Temporal - 现代化的日期/时间 API (提案中)
js
// Temporal 提案 (预计ES16)
// 注意: 以下代码基于当前提案阶段,最终实现可能有所不同
// 创建日期
const today = Temporal.Now.plainDateISO();
console.log(today.toString()); // 输出: 2023-10-15 (示例日期)
// 日期计算
const tomorrow = today.add({ days: 1 });
console.log(tomorrow.toString()); // 输出: 2023-10-16
// 时间计算
const now = Temporal.Now.plainTimeISO();
const meeting = now.add({ hours: 2, minutes: 30 });
console.log(meeting.toString()); // 输出: 14:30:00 (示例时间)
// 处理不同时区
const dateTimeInTokyo = Temporal.Now.zonedDateTimeISO('Asia/Tokyo');
console.log(dateTimeInTokyo.toString());
装饰器 (Decorators) - 类和类元素的元编程 (提案中)
js
// 装饰器提案 (预计ES16)
// 注意: 以下代码基于当前提案阶段,最终实现可能有所不同
// 方法装饰器
function log(target, name, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${name} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`Method ${name} returned:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
calc.add(5, 3); // 会输出调用日志