作为一名前端开发者,熟练运用 ES6+ 特性是基本功。本文精选最常用、最实用的JavaScript 新特性,结合实际开发场景,帮你攻克开发难点,提升代码质量。告别繁琐的老式写法,拥抱现代JavaScript。
1. 解构赋值:优雅地获取数据
// 对象解构
const user = {
name: 'Tom',
age: 25,
address: {
city: 'Shanghai',
street: 'Nanjing Road'
}
};
// 基础解构
const { name, age } = user;
// 深层解构
const { address: { city } } = user;
// 设置默认值
const { country = 'China' } = user;
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
/* 1. 解构赋值:实战应用 */
// 场景1: 接口数据处理
const handleResponse = (response) => {
const { code, data: { userInfo, permissions } } = response;
return code === 200 ? userInfo : null;
};
// 场景2: React组件属性处理
function UserCard({ name, avatar, role = 'user', ...props }) {
return (/* 组件内容 */);
}
接口处理:后端返回数据结构发生异常时(如从数组变为null),解构赋值会抛出错误。这是因为解构要求目标具有特定的数据结构,即使在错误状态下,数据类型也应保持一致。比如API正常返回数组时使用了解构,那么在异常情况下也应返回空数组[],而不是null或其他类型。这样可以确保解构操作的安全性,并保持类型的一致性。
2. 展开运算符:代码更简洁
// 对象合并
const defaultConfig = { theme: 'dark', lang: 'en' };
const userConfig = { theme: 'light' };
const config = { ...defaultConfig, ...userConfig };
// 数组操作
const nums = [1, 2, 3];
const moreNums = [...nums, 4, 5]; // [1, 2, 3, 4, 5]
// 函数参数
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
/* 展开运算符:实战应用 */
// 场景1: 配置合并
const mergeConfig = (defaultConfig, userConfig) => {
return {
...defaultConfig,
...userConfig,
timestamp: Date.now()
};
};
// 场景2: 不可变数据处理
const addTodo = (todos, newTodo) => {
return [...todos, newTodo];
};
3. 可选链操作符:告别层层判断
// 之前的写法
const street = user && user.address && user.address.street;
// 现代写法
const street = user?.address?.street;
// 数组使用
const first = arr?.[0];
// 函数调用
const result = func?.();
/* 可选链:实战应用 */
// 场景1: 嵌套数据访问
const getUserCity = (user) => {
return user?.address?.city ?? '未知城市';
};
// 场景2: 函数安全调用
const logEvent = (analytics) => {
analytics?.logEvent?.('page_view');
};
4. 空值合并:更智能的默认值
// 之前的写法
const value = data || 'default'; // 0, '', false 都会被替换
// 现代写法
const value = data ?? 'default'; // 只有 null 和 undefined 会被替换
/* 空值合并:实战应用 */
// 场景1: 表单默认值
const getFormValue = (form) => {
return {
name: form.name ?? '匿名用户',
age: form.age ?? 18,
city: form.city ?? '未知'
};
};
// 场景2: 配置参数
const initConfig = (config) => {
return {
theme: config.theme ?? 'light',
lang: config.lang ?? 'zh-CN',
timeout: config.timeout ?? 3000
};
};
5. 模板字符串:更强大的字符串处理
const name = 'Tom';
const age = 25;
// 基础用法
const greeting = `Hello ${name}, you are ${age} years old!`;
// 多行文本
const html = `
<div>
<h1>${name}</h1>
<p>Age: ${age}</p>
</div>
`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) =>
`${result}${str}${values[i] ? `<span class="highlight">${values[i]}</span>` : ''}`
, '');
}
const highlighted = highlight`The name is ${name}!`;
/* 模板字符串:实战应用 */
// 场景1: 动态消息生成
const createMessage = (user, action) => {
return `用户 ${user.name} 在 ${new Date().toLocaleString()} ${action}`;
};
// 场景2: URL构建
const buildApiUrl = (endpoint, params) => {
return `${API_BASE}/${endpoint}?token=${params.token}`;
};
6. Promise 和 async/await:优雅的异步处理
// Promise 链式调用
fetch('/api/user')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// async/await 写法
async function getUser() {
try {
const response = await fetch('/api/user');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
// Promise 并行
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
/* Promise和async/await:实战应用 */
// 场景1: 并发请求
const loadDashboard = async () => {
const [users, orders, stats] = await Promise.all([
fetchUsers(),
fetchOrders(),
fetchStats()
]);
return { users, orders, stats };
};
// 场景2: 错误重试
const fetchWithRetry = async (url, retries = 3) => {
for(let i = 0; i < retries; i++) {
try {
return await fetch(url);
} catch(err) {
if (i === retries - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * i));
}
}
};
7. 对象和数组的新方法
// Object 新方法
const obj = { name: 'Tom', age: 25 };
Object.entries(obj); // [['name', 'Tom'], ['age', 25]]
Object.fromEntries([['name', 'Tom'], ['age', 25]]); // { name: 'Tom', age: 25 }
// Array 新方法
const arr = [1, 2, 3, 4, 5];
// find 和 findLast
const found = arr.find(x => x > 3); // 4
const foundLast = arr.findLast(x => x > 3); // 5
// flat 和 flatMap
const nested = [1, [2, 3], [4, [5]]];
nested.flat(2); // [1, 2, 3, 4, 5]
// at - 支持负数索引
arr.at(-1); // 5
/* 对象和数组新方法:实战应用 */
// 场景1: 数据转换
const arrayToObject = (array) => {
return Object.fromEntries(
array.map(item => [item.id, item])
);
};
// 场景2: 分页数据处理
const getPageItems = (items, page, pageSize) => {
return items.slice((page - 1) * pageSize).slice(0, pageSize);
};
8. 类的私有字段和私有方法
class User {
#privateField = 'private';
#privateMethod() {
return 'private method';
}
publicMethod() {
console.log(this.#privateField);
console.log(this.#privateMethod());
}
}
/* 8. 类的私有字段:实战应用 */
// 场景1: 缓存管理器
class CacheManager {
#cache = new Map();
#maxAge;
constructor(maxAge = 3600000) {
this.#maxAge = maxAge;
}
set(key, value) {
this.#cache.set(key, {
value,
timestamp: Date.now()
});
}
}
// 场景2: 状态管理
class Store {
#state = {};
#listeners = new Set();
subscribe(listener) {
this.#listeners.add(listener);
return () => this.#listeners.delete(listener);
}
}
9. Map 和 Set 数据结构
// Map - 键值对集合,任何类型都可以作为键
const map = new Map();
map.set('key', 'value');
map.set(objKey, 'value'); // 对象也可以作为键
// Map 的常用操作
for (let [key, value] of map) {
console.log(`${key} = ${value}`);
}
// WeakMap - 弱引用版本的Map
const weakMap = new WeakMap(); // 用于防止内存泄漏
// Set - 值的集合,自动去重
const set = new Set([1, 2, 2, 3, 3]); // {1, 2, 3}
// Set 的实用操作
const intersection = new Set([...set1].filter(x => set2.has(x)));
const difference = new Set([...set1].filter(x => !set2.has(x)));
/* Map和Set:实战应用 */
// 场景1: 用户会话管理
class SessionManager {
#sessions = new Map();
addSession(userId, token) {
this.#sessions.set(userId, {
token,
lastActive: Date.now()
});
}
}
// 场景2: unique标签系统
class TagManager {
#tags = new Set();
addTags(tags) {
tags.forEach(tag => this.#tags.add(tag));
return Array.from(this.#tags);
}
}
10. 数组高级操作
// 数组分组(ES2023)
const items = [
{type: 'fruit', name: 'apple'},
{type: 'vegetable', name: 'carrot'},
{type: 'fruit', name: 'banana'},
];
const grouped = Object.groupBy(items, item => item.type);
/*
{
fruit: [{type: 'fruit', name: 'apple'}, {type: 'fruit', name: 'banana'}],
vegetable: [{type: 'vegetable', name: 'carrot'}]
}
*/
// 数组操作新方法
const arr = [1, 2, 3, 4, 5];
// toSorted, toReversed - 不修改原数组的版本
const sorted = arr.toSorted((a, b) => b - a);
const reversed = arr.toReversed();
// with - 不修改原数组的替换
const newArr = arr.with(2, 10); // [1, 2, 10, 4, 5]
/* 数组高级操作:实战应用 */
// 场景1: 分页列表排序(不改变原数组)
const sortPageItems = (items, sortKey, sortOrder = 'asc') => {
return items.toSorted((a, b) => {
return sortOrder === 'asc'
? a[sortKey] - b[sortKey]
: b[sortKey] - a[sortKey];
});
};
// 场景2: 轮播图片索引
class Carousel {
#images = ['1.jpg', '2.jpg', '3.jpg', '4.jpg'];
#currentIndex = 0;
prev() {
// 使用at处理负数索引,轻松获取末尾元素
return this.#images.at(--this.#currentIndex);
}
next() {
return this.#images.at(++this.#currentIndex % this.#images.length);
}
}
// 场景3: 列表更新(不修改原数组)
const updateListItem = (list, index, newValue) => {
return list.with(index, newValue);
};
// 场景4: 反转展示历史记录
class History {
#records = [];
add(record) {
this.#records.push(record);
}
// 最新的记录先显示
getRecentRecords(limit = 10) {
return this.#records.toReversed().slice(0, limit);
}
}
// 场景5: 数据导出排序
const exportSortedData = (data) => {
// 不影响原始数据的排序
const sortedData = data.toSorted((a, b) => a.name.localeCompare(b.name));
return sortedData;
};
11. 字符串新特性
// replaceAll
const str = 'hello hello';
str.replaceAll('hello', 'hi'); // 'hi hi'
// matchAll
const regex = /t(e)(st(\d?))/g;
const text = 'test1 test2 test3';
const matches = [...text.matchAll(regex)];
// padStart/padEnd
'5'.padStart(2, '0'); // '05'
'hello'.padEnd(10, '*'); // 'hello*****'
/* 字符串新特性:实战应用 */
// 场景1: 格式化处理
const formatAmount = (amount) => {
return amount.toString().padStart(3, '0');
};
// 场景2: 模板替换
const replaceTemplate = (template, data) => {
return template.replaceAll(
/\{\{(\w+)\}\}/g,
(_, key) => data[key] ?? ''
);
};
12. 类的新特性
class Product {
// 公共字段声明
name;
price;
// 私有字段
#stock;
// 静态私有字段
static #count = 0;
// 静态公共方法
static createProduct(name, price) {
return new Product(name, price);
}
// 类字段初始化器
tax = 0.1;
// getter/setter
get inStock() {
return this.#stock > 0;
}
// 私有方法
#calculateDiscount() {
return this.price * 0.1;
}
}
/* 类的新特性:实战应用 */
// 场景1: 表单验证器
class FormValidator {
static #rules = new Map();
static addRule(name, validator) {
this.#rules.set(name, validator);
}
validate(data) {
// 验证逻辑
}
}
13. 高级函数特性
// 函数参数默认值
function greet(name = 'Guest', greeting = `Hello`) {
return `${greeting}, ${name}!`;
}
// 箭头函数和this
class Handler {
constructor() {
this.value = 42;
}
normal() {
setTimeout(function() {
console.log(this.value); // undefined
});
}
arrow() {
setTimeout(() => {
console.log(this.value); // 42
});
}
}
// 函数组合
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
const add10 = x => x + 10;
const multiply2 = x => x * 2;
const combined = pipe(add10, multiply2);
/* 13. 高级函数特性:实战应用 */
// 场景1: API请求装饰器
const withLoading = (fn) => async (...args) => {
try {
showLoading();
return await fn(...args);
} finally {
hideLoading();
}
};
// 场景2: 函数管道
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
14. 正则表达式增强
// 命名捕获组
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2024-02-07'.match(regex);
console.log(match.groups.year); // 2024
// 后行断言
const price = '$123.45'.match(/(?<=\$)\d+\.\d+/)[0];
// Unicode 属性转义
const regex = /\p{Emoji}/u;
console.log(regex.test('🎉')); // true
/* 正则表达式增强:实战应用 */
// 场景1: 日期解析
const parseDate = (dateStr) => {
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { groups } = dateStr.match(regex);
return new Date(groups.year, groups.month - 1, groups.day);
};
// 场景2: 金额提取
const extractAmount = (text) => {
return text.match(/(?<=\$)\d+(\.\d{2})?/)[0];
};
15. 模块化高级特性
// 动态导入
const loadModule = async () => {
const module = await import('./module.js');
module.doSomething();
};
// 模块命名空间导出
export * as utils from './utils.js';
// 导出默认值和具名导出组合
export { default as Main, utilities } from './module.js';
/* 模块化高级特性:实战应用 */
// 场景1: 动态加载
const loadModule = async (moduleName) => {
const module = await import(`./modules/${moduleName}.js`);
return module.default;
};
// 场景2: 统一导出
export * as validators from './validators.js';
16. 实用的编程模式
// 管道操作
const double = n => n * 2;
const increment = n => n + 1;
const square = n => n * n;
// 函数式编程管道
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const compute = pipe(double, increment, square);
console.log(compute(3)); // ((3 * 2 + 1) ^ 2) = 49
// 装饰器模式(Stage 3提案)
function log(target, name, descriptor) {
// 方法装饰器
}
// 对象属性监听
const handler = {
get: function(target, prop) {
console.log(`Accessing ${prop}`);
return target[prop];
}
};
const proxy = new Proxy({}, handler);
/* 实用编程模式:实战应用 */
// 场景1: 状态管理
const createStore = (reducer) => {
let state = reducer(undefined, {});
const listeners = new Set();
return {
getState: () => state,
dispatch: (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
},
subscribe: (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
}
};
};
17. 错误处理最佳实践
// 自定义错误类
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
// 异步错误处理
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new ValidationError('API请求失败');
}
return await response.json();
} catch (error) {
if (error instanceof ValidationError) {
// 处理验证错误
} else {
// 处理其他错误
}
} finally {
// 清理工作
}
}
/* 错误处理最佳实践:实战应用 */
// 场景1: 异步错误边界
const withErrorBoundary = async (fn) => {
try {
return await fn();
} catch (error) {
if (error instanceof ApiError) {
handleApiError(error);
} else {
reportError(error);
}
throw error;
}
};
实用技巧
1. 链式可选操作
// 组合使用可选链和空值合并
const username = data?.user?.profile?.name ?? 'Anonymous';
2. 动态对象属性
const field = 'name';
const value = 'Tom';
const user = {
[field]: value
};
3. 数组去重
const unique = [...new Set([1, 1, 2, 3, 3])]; // [1, 2, 3]
注意事项
- 解构赋值和展开运算符可能影响性能,处理大数据时需谨慎
- 可选链在频繁调用时可能影响性能,考虑先缓存中间结果
- Promise.all() 如果任一promise失败,整个都会失败,考虑使用 Promise.allSettled()
总结扩展
现代JavaScript的这些特性不仅让代码更简洁,还提供了很多强大的功能:
- 使用新的数据结构(Map/Set)来处理复杂数据关系
- 利用代理和反射实现更灵活的对象操作
- 通过新的数组方法简化数据处理
- 使用模块化特性组织更好的代码结构
- 通过装饰器和私有字段实现更好的封装
建议:
- 循序渐进地在项目中引入新特性
- 注意浏览器兼容性,必要时使用Babel转译
- 关注性能影响,某些特性在大规模使用时可能影响性能
- 保持学习最新的提案和特性更新