ES6-ES2020+新特性
ES6 (ES2015) 主要特性
1. let 和 const 声明
let
: 块级作用域变量声明const
: 块级作用域常量声明
javascript
// let 示例
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// const 示例
const API_URL = 'https://api.example.com';
const user = { name: 'John' };
user.name = 'Jane'; // 可以修改对象属性
// user = {}; // 错误:不能重新赋值
2. 箭头函数
简化函数语法,自动绑定 this
javascript
// 传统函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => a + b;
// 复杂箭头函数
const processUsers = (users) => {
return users
.filter((user) => user.active)
.map((user) => ({
...user,
displayName: `${user.firstName} ${user.lastName}`,
}));
};
3. 模板字符串
使用反引号创建字符串,支持变量插值和多行
javascript
const name = 'World';
const greeting = `Hello, ${name}!`;
const multiLine = `
这是一个
多行字符串
示例
`;
// 标记模板字符串
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
return result + string + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const user = 'John';
const age = 30;
const html = highlight`用户 ${user} 年龄 ${age} 岁`;
4. 解构赋值
从数组或对象中提取值并赋给变量
javascript
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first, second, rest); // 1, 2, [3, 4, 5]
// 对象解构
const user = { name: 'John', age: 30, city: 'New York' };
const { name, age, city = 'Unknown' } = user;
// 函数参数解构
function greet({ name, age = 0 }) {
return `Hello, ${name}! You are ${age} years old.`;
}
// 嵌套解构
const data = {
user: {
profile: {
name: 'John',
settings: { theme: 'dark' },
},
},
};
const {
user: {
profile: {
name,
settings: { theme },
},
},
} = data;
5. 默认参数
为函数参数设置默认值
javascript
function greet(name = 'Guest', greeting = 'Hello') {
return `${greeting}, ${name}!`;
}
// 使用其他参数作为默认值
function createUser(name, role = 'user', permissions = getDefaultPermissions(role)) {
return { name, role, permissions };
}
function getDefaultPermissions(role) {
return role === 'admin' ? ['read', 'write', 'delete'] : ['read'];
}
6. 扩展运算符 (Spread Operator)
展开数组、对象或字符串
javascript
// 数组扩展
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 对象扩展
const user = { name: 'John', age: 30 };
const updatedUser = { ...user, age: 31, city: 'New York' };
// 函数调用
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// 字符串扩展
const chars = [...'Hello']; // ['H', 'e', 'l', 'l', 'o']
7. 剩余参数 (Rest Parameters)
收集剩余参数为数组
javascript
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// 与普通参数结合
function greetUsers(greeting, ...names) {
return names.map((name) => `${greeting}, ${name}!`);
}
8. 类 (Classes)
面向对象编程的语法糖
javascript
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
static createAdult(name) {
return new Person(name, 18);
}
}
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
study() {
return `${this.name} is studying`;
}
greet() {
return `${super.greet()}, I'm in grade ${this.grade}`;
}
}
const student = new Student('Alice', 16, 10);
9. 模块 (Modules)
ES6 模块系统
javascript
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default function multiply(a, b) {
return a * b;
}
// main.js
import multiply, { PI, add } from './math.js';
import * as math from './math.js';
// 动态导入
async function loadModule() {
const module = await import('./math.js');
return module.default(2, 3);
}
10. Promise
异步编程解决方案
javascript
// 创建 Promise
const fetchData = (url) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url) {
resolve({ data: 'Success' });
} else {
reject(new Error('URL is required'));
}
}, 1000);
});
};
// 使用 Promise
fetchData('https://api.example.com')
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log('请求完成'));
// Promise.all
Promise.all([fetchData('url1'), fetchData('url2'), fetchData('url3')]).then((results) => {
console.log('所有请求完成:', results);
});
11. Map 和 Set
新的数据结构
javascript
// Map
const userRoles = new Map();
userRoles.set('john', 'admin');
userRoles.set('jane', 'user');
userRoles.set(123, 'guest'); // 可以使用任何类型作为键
console.log(userRoles.get('john')); // 'admin'
console.log(userRoles.has('jane')); // true
console.log(userRoles.size); // 3
// Set
const uniqueNumbers = new Set([1, 2, 2, 3, 3, 4]);
console.log(uniqueNumbers); // Set { 1, 2, 3, 4 }
uniqueNumbers.add(5);
uniqueNumbers.delete(1);
console.log(uniqueNumbers.has(2)); // true
12. Symbol
新的原始数据类型
javascript
// 创建 Symbol
const id = Symbol('id');
const id2 = Symbol('id');
console.log(id === id2); // false
// 对象属性
const user = {
name: 'John',
[id]: 123,
};
// 内置 Symbol
const obj = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
},
};
for (const value of obj) {
console.log(value); // 1, 2, 3
}
ES2016 (ES7) 主要特性
1. Array.prototype.includes()
检查数组是否包含某个元素
javascript
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape')); // false
// 指定起始位置
console.log(fruits.includes('apple', 1)); // false
// 处理 NaN
const numbers = [1, 2, NaN, 4];
console.log(numbers.includes(NaN)); // true
2. 指数运算符 (**)
幂运算的简化语法
javascript
// 传统方式
console.log(Math.pow(2, 3)); // 8
// ES2016 方式
console.log(2 ** 3); // 8
console.log(2 ** (3 ** 2)); // 2 ** (3 ** 2) = 2 ** 9 = 512
// 复合赋值
let num = 2;
num **= 3;
console.log(num); // 8
ES2017 (ES8) 主要特性
1. async/await
异步函数的语法糖
javascript
// 使用 async/await
async function fetchUserData(userId) {
try {
const user = await fetch(`/users/${userId}`);
const userData = await user.json();
const posts = await fetch(`/users/${userId}/posts`);
const postsData = await posts.json();
return {
user: userData,
posts: postsData,
};
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// 并行执行
async function fetchMultipleUsers(userIds) {
const promises = userIds.map((id) => fetchUserData(id));
const results = await Promise.all(promises);
return results;
}
2. Object.values() 和 Object.entries()
获取对象的值和键值对
javascript
const user = {
name: 'John',
age: 30,
city: 'New York',
};
// Object.values()
console.log(Object.values(user)); // ['John', 30, 'New York']
// Object.entries()
console.log(Object.entries(user));
// [['name', 'John'], ['age', 30], ['city', 'New York']]
// 实际应用
Object.entries(user).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// 转换为 Map
const userMap = new Map(Object.entries(user));
3. String.prototype.padStart() 和 padEnd()
字符串填充方法
javascript
// padStart()
const num = '5';
console.log(num.padStart(3, '0')); // '005'
console.log(num.padStart(5, 'x')); // 'xxxx5'
// padEnd()
const str = 'Hello';
console.log(str.padEnd(10, '.')); // 'Hello.....'
// 实际应用:格式化时间
function formatTime(hours, minutes) {
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
}
console.log(formatTime(9, 5)); // '09:05'
4. Object.getOwnPropertyDescriptors()
获取对象所有自有属性的描述符
javascript
const obj = {
name: 'John',
get age() {
return this._age;
},
set age(value) {
this._age = value;
},
};
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 浅拷贝包括 getter/setter
const copy = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
5. 函数参数列表和调用中的尾随逗号
允许在参数列表末尾添加逗号
javascript
function greet(
name,
age,
city // 尾随逗号
) {
return `Hello ${name}`;
}
greet(
'John',
30,
'New York' // 尾随逗号
);
ES2018 (ES9) 主要特性
1. 异步迭代器 (for-await-of)
处理异步可迭代对象
javascript
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
async function processAsyncData() {
for await (const value of asyncGenerator()) {
console.log(value); // 1, 2, 3
}
}
// 处理多个异步操作
async function processFiles(fileUrls) {
for await (const url of fileUrls) {
const response = await fetch(url);
const data = await response.text();
console.log(`处理文件: ${url}`);
}
}
2. Rest/Spread 属性
对象的剩余和扩展属性
javascript
// Rest 属性
const user = {
name: 'John',
age: 30,
email: 'john@example.com',
phone: '123-456-7890',
};
const { name, age, ...contactInfo } = user;
console.log(contactInfo); // { email: '...', phone: '...' }
// Spread 属性
const defaultSettings = { theme: 'light', language: 'en' };
const userSettings = { language: 'zh', notifications: true };
const finalSettings = { ...defaultSettings, ...userSettings };
// { theme: 'light', language: 'zh', notifications: true }
// 函数中使用
function updateUser(id, updates) {
const currentUser = getUserById(id);
return { ...currentUser, ...updates, updatedAt: new Date() };
}
3. Promise.prototype.finally()
无论 Promise 状态如何都会执行的回调
javascript
function fetchData() {
setLoading(true);
return fetch('/api/data')
.then((response) => response.json())
.then((data) => {
setData(data);
return data;
})
.catch((error) => {
setError(error);
throw error;
})
.finally(() => {
setLoading(false); // 无论成功失败都会执行
});
}
4. 正则表达式改进
命名捕获组
javascript
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2023-12-25'.match(regex);
console.log(match.groups); // { year: '2023', month: '12', day: '25' }
// 替换中使用命名组
const newDate = '2023-12-25'.replace(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/, '$<day>/$<month>/$<year>');
console.log(newDate); // '25/12/2023'
dotAll 标志
javascript
// 传统方式:. 不匹配换行符
const text = 'Hello\nWorld';
console.log(/Hello.World/.test(text)); // false
// 使用 dotAll 标志
console.log(/Hello.World/s.test(text)); // true
Unicode 属性转义
javascript
// 匹配所有表情符号
const emojiRegex = /\p{Emoji}/u;
console.log(emojiRegex.test('😀')); // true
// 匹配中文字符
const chineseRegex = /\p{Script=Han}/u;
console.log(chineseRegex.test('中文')); // true
后行断言
javascript
// 正向后行断言
const regex1 = /(?<=\$)\d+/;
console.log('$100'.match(regex1)); // ['100']
// 负向后行断言
const regex2 = /(?<!\$)\d+/;
console.log('100 $200'.match(regex2)); // ['100']
ES2019 (ES10) 主要特性
1. Array.prototype.flat() 和 flatMap()
数组扁平化方法
javascript
// flat() - 扁平化数组
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
console.log(nested.flat(Infinity)); // 完全扁平化
// flatMap() - 映射后扁平化
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap((sentence) => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'How', 'are', 'you']
// 实际应用
const users = [
{ name: 'John', hobbies: ['reading', 'gaming'] },
{ name: 'Jane', hobbies: ['cooking', 'painting'] },
];
const allHobbies = users.flatMap((user) => user.hobbies);
console.log(allHobbies); // ['reading', 'gaming', 'cooking', 'painting']
2. Object.fromEntries()
将键值对列表转换为对象
javascript
// 从数组创建对象
const entries = [
['name', 'John'],
['age', 30],
];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'John', age: 30 }
// 从 Map 创建对象
const map = new Map([
['a', 1],
['b', 2],
]);
const objFromMap = Object.fromEntries(map);
// 实际应用:过滤对象属性
const user = { name: 'John', age: 30, password: '123', email: 'john@example.com' };
const publicUser = Object.fromEntries(Object.entries(user).filter(([key]) => key !== 'password'));
console.log(publicUser); // { name: 'John', age: 30, email: '...' }
3. String.prototype.trimStart() 和 trimEnd()
字符串首尾空白符清理
javascript
const str = ' Hello World ';
console.log(str.trimStart()); // 'Hello World '
console.log(str.trimEnd()); // ' Hello World'
console.log(str.trim()); // 'Hello World'
// 别名(为了向后兼容)
console.log(str.trimLeft()); // 等同于 trimStart()
console.log(str.trimRight()); // 等同于 trimEnd()
4. try...catch 可选的绑定参数
catch 子句不需要错误参数
javascript
// ES2019 之前
try {
JSON.parse(invalidJSON);
} catch (error) {
// 即使不使用 error,也必须声明
console.log('JSON 解析失败');
}
// ES2019
try {
JSON.parse(invalidJSON);
} catch {
// 可以省略错误参数
console.log('JSON 解析失败');
}
5. 稳定的 Array.prototype.sort()
确保排序的稳定性
javascript
const items = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 30 },
];
// 按年龄排序,相同年龄的保持原有顺序
const sorted = items.sort((a, b) => a.age - b.age);
// 稳定排序确保 John 在 Bob 之前(原有顺序)
ES2020 (ES11) 主要特性
1. 可选链 (Optional Chaining)
安全访问深层嵌套的对象属性
javascript
const user = {
profile: {
personal: {
name: 'John',
},
},
};
// 传统方式
const name = user && user.profile && user.profile.personal && user.profile.personal.name;
// 可选链
const name = user?.profile?.personal?.name;
const age = user?.profile?.personal?.age ?? 'Unknown';
// 方法调用
user?.profile?.updateProfile?.();
// 数组索引
const firstHobby = user?.hobbies?.[0];
// 动态属性
const prop = 'personal';
const info = user?.profile?.[prop]?.name;
2. 空值合并 (Nullish Coalescing)
处理 null 和 undefined 值
javascript
// 只有在 null 或 undefined 时才使用默认值
const name = user.name ?? 'Guest';
const count = user.count ?? 0; // 即使 count 是 0 也不会使用默认值
// 与 || 的区别
const value1 = user.value || 'default'; // falsy 值都会使用默认值
const value2 = user.value ?? 'default'; // 只有 null/undefined 使用默认值
console.log(0 || 'default'); // 'default'
console.log(0 ?? 'default'); // 0
console.log('' || 'default'); // 'default'
console.log('' ?? 'default'); // ''
console.log(false || 'default'); // 'default'
console.log(false ?? 'default'); // false
3. BigInt
任意精度整数
javascript
// 创建 BigInt
const bigNum1 = 123n;
const bigNum2 = BigInt('123456789012345678901234567890');
// 运算
console.log(bigNum1 + 456n); // 579n
console.log(bigNum2 * 2n);
// 注意:不能混合 BigInt 和 Number
// console.log(bigNum1 + 123); // 错误
console.log(bigNum1 + BigInt(123)); // 正确
// 比较
console.log(10n > 5); // true
console.log(10n === 10); // false
console.log(10n == 10); // true
// 实际应用:处理大整数 ID
const userId = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
4. 动态导入 (Dynamic Import)
运行时动态加载模块
javascript
// 条件导入
async function loadFeature(featureName) {
if (featureName === 'chart') {
const { Chart } = await import('./chart.js');
return new Chart();
} else if (featureName === 'table') {
const { Table } = await import('./table.js');
return new Table();
}
}
// 懒加载
button.addEventListener('click', async () => {
const { heavyFunction } = await import('./heavy-module.js');
heavyFunction();
});
// 错误处理
try {
const module = await import('./module.js');
module.init();
} catch (error) {
console.error('模块加载失败:', error);
}
5. globalThis
全局对象的标准化访问
javascript
// 在不同环境中访问全局对象
// 浏览器: window
// Node.js: global
// Web Workers: self
// 统一的全局对象访问
const global = globalThis;
// 安全地访问全局变量
const fetch = globalThis.fetch || require('node-fetch');
6. Promise.allSettled()
等待所有 Promise 完成(无论成功失败)
javascript
const promises = [
fetch('/api/user/1'),
fetch('/api/user/2'),
fetch('/api/invalid-url'), // 这个会失败
];
// Promise.all 在任何一个失败时就会 reject
// Promise.allSettled 会等待所有完成
Promise.allSettled(promises).then((results) => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`请求 ${index} 成功:`, result.value);
} else {
console.log(`请求 ${index} 失败:`, result.reason);
}
});
});
// 实际应用:批量处理用户数据
async function processUsers(userIds) {
const userPromises = userIds.map((id) => fetchUser(id));
const results = await Promise.allSettled(userPromises);
const successfulUsers = results.filter((result) => result.status === 'fulfilled').map((result) => result.value);
const failedUserIds = results
.map((result, index) => (result.status === 'rejected' ? userIds[index] : null))
.filter(Boolean);
return { successfulUsers, failedUserIds };
}
7. String.prototype.matchAll()
全局匹配所有正则表达式结果
javascript
const text = 'test1 test2 test3';
const regex = /test(\d)/g;
// 传统方式
let match;
const matches = [];
while ((match = regex.exec(text)) !== null) {
matches.push(match);
}
// 使用 matchAll
const matches = [...text.matchAll(regex)];
console.log(matches);
// [
// ['test1', '1', index: 0, input: 'test1 test2 test3'],
// ['test2', '2', index: 6, input: 'test1 test2 test3'],
// ['test3', '3', index: 12, input: 'test1 test2 test3']
// ]
// 实际应用:提取所有链接
const html = '<a href="/page1">Link1</a> <a href="/page2">Link2</a>';
const linkRegex = /<a href="([^"]+)">([^<]+)<\/a>/g;
const links = [...html.matchAll(linkRegex)].map((match) => ({
url: match[1],
text: match[2],
}));
8. 标准化的 for-in 枚举顺序
确保 for-in 循环的属性枚举顺序
javascript
const obj = {
2: 'two',
1: 'one',
b: 'letter b',
a: 'letter a',
};
// ES2020 保证了枚举顺序
for (const key in obj) {
console.log(key); // '1', '2', 'a', 'b'
}
// 数字键按数值排序,字符串键按创建顺序
总结
重要性排序
- ES6: 革命性更新,引入了现代 JavaScript 的基础
- ES2017: async/await 大幅改善异步编程体验
- ES2020: 可选链和空值合并解决了常见的安全访问问题
- ES2018: Rest/Spread 属性完善了对象操作
- ES2019: 数组扁平化和对象转换方法
实际开发建议
- 优先掌握: ES6 基础、async/await、可选链、空值合并
- 逐步采用: 根据项目需求和浏览器支持情况选择性使用新特性
- 工具支持: 使用 Babel 等工具确保兼容性
- 性能考虑: 了解各特性的性能影响,合理选择
持续学习
JavaScript 标准每年都会更新,建议:
- 关注 TC39 提案进展
- 实践新特性在实际项目中的应用
- 了解浏览器和 Node.js 的支持情况
- 参与社区讨论和最佳实践分享