🚀 ES6特性全攻略:让你的JavaScript代码更优雅
ES6(ECMAScript 2015)是JavaScript历史上最重要的更新之一,它不仅带来了语法糖,更重要的是改变了我们编写JavaScript的思维方式。本文将通过生动的例子和实际场景,带你领略ES6的魅力!
一、变量声明革命:告别var的混乱时代 🌟
还记得那些被var坑过的日子吗?变量提升、作用域混乱…ES6的let和const终于拯救了我们!
1 2 3 4 5 6 7 8 9 10
| for (var i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 100); }
console.log(name); var name = "Alice";
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 100); }
console.log(age); let age = 25;
const API_URL = "https://api.example.com";
|
最佳实践:默认使用const,需要重新赋值时才用let,彻底告别var!
二、解构赋值:数据提取的艺术 🎯
想象一下,你是一个快递员,需要从包裹中取出不同的物品。解构赋值就像是给你一个神奇的工具,让你一次性精准地取出所需的东西!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const position = [100, 200, 50]; const [x, y, z = 0] = position;
let a = 1, b = 2; [a, b] = [b, a];
const scores = [95, 87, 92, 78, 88]; const [highest, ...others] = scores.sort((a, b) => b - a); console.log(`最高分: ${highest}, 其他: ${others}`);
function getNameAndAge() { return ["Alice", 25]; } const [name, age] = getNameAndAge();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const response = { data: { user: { id: 1, profile: { name: "Bob", avatar: "avatar.jpg" } } }, status: 200 };
const { data: { user: { profile: { name: userName, avatar: userAvatar = "default.jpg" } } }, status = 500 } = response;
function UserCard({ name, age, avatar = "default.jpg", ...otherProps }) { return `<div>Hello ${name}, ${age} years old</div>`; }
const config = { theme: "dark", language: "zh-CN", notifications: true };
const { theme, ...settings } = config;
|
解构赋值的超能力:
- 🚀 让代码更简洁易读
- 🎯 精准提取所需数据
- 🛡️ 提供默认值保护
- 🔄 轻松实现变量交换
三、模板字符串:告别字符串拼接地狱 🎨
还在用+号拼接字符串?还在为换行而烦恼?模板字符串来拯救你了!它不仅仅是语法糖,更是字符串处理的革命。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const name = "Alice"; const age = 25; const oldWay = "Hello, " + name + "!\n" + "You are " + age + " years old.\n" + "Today is " + new Date().toLocaleDateString();
const newWay = `Hello, ${name}! You are ${age} years old. Today is ${new Date().toLocaleDateString()}`;
const price = 99.9; const tax = 0.1; const receipt = ` 商品价格: ¥${price} 税费: ¥${(price * tax).toFixed(2)} 总计: ¥${(price * (1 + tax)).toFixed(2)} `;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| function createUserCard(user) { return ` <div class="user-card ${user.isVip ? 'vip' : 'normal'}"> <img src="${user.avatar}" alt="${user.name}"> <h3>${user.name}</h3> <p>积分: ${user.points.toLocaleString()}</p> ${user.isVip ? '<span class="vip-badge">VIP</span>' : ''} </div> `; }
function generateEmail(order) { return ` 亲爱的 ${order.customerName}, 您的订单 #${order.id} 已确认! 订单详情: ${order.items.map(item => `- ${item.name} x${item.quantity} = ¥${item.total}` ).join('\n ')} 总金额:¥${order.total} 预计送达:${order.deliveryDate} 感谢您的购买! `; }
function buildQuery(table, conditions) { const whereClause = Object.entries(conditions) .map(([key, value]) => `${key} = '${value}'`) .join(' AND '); return ` SELECT * FROM ${table} WHERE ${whereClause} ORDER BY created_at DESC LIMIT 10; `; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function highlight(strings, ...values) { return strings.reduce((result, string, i) => { const value = values[i] ? `<mark>${values[i]}</mark>` : ''; return result + string + value; }, ''); }
const searchTerm = "JavaScript"; const text = highlight`学习 ${searchTerm} 是前端开发的必经之路`;
function createStyles(theme) { return ` .button { background: ${theme.primary}; color: ${theme.text}; border-radius: ${theme.borderRadius}px; padding: ${theme.spacing * 2}px ${theme.spacing * 3}px; transition: all 0.3s ease; } .button:hover { background: ${theme.primaryHover}; transform: translateY(-2px); } `; }
|
模板字符串的魔法:
- 🎯 表达式插值:
${expression}
- 📝 多行字符串:自然换行
- 🏷️ 标签模板:自定义处理逻辑
- 🎨 动态内容:完美适配现代开发
四、展开运算符:三个点的无限可能 🌟
三个小点...,却有着巨大的能量!展开运算符就像是数据世界的瑞士军刀,一个工具解决多种问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const fruits = ['🍎', '🍌']; const vegetables = ['🥕', '🥬']; const food = [...fruits, '🍖', ...vegetables];
const original = [1, 2, 3]; const copy = [...original];
const scores = [89, 95, 76, 92, 88]; const highest = Math.max(...scores); const lowest = Math.min(...scores);
const numbers = [1, 2, 2, 3, 3, 4]; const unique = [...new Set(numbers)];
const list = ['a', 'b', 'e']; const newList = ['a', 'b', 'c', 'd', 'e'];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| const defaultConfig = { theme: 'light', language: 'en', autoSave: true, timeout: 5000 };
const userConfig = { theme: 'dark', timeout: 10000 };
const finalConfig = { ...defaultConfig, ...userConfig };
function createUser(name, age, isAdmin = false) { return { name, age, createdAt: new Date(), ...(isAdmin && { role: 'admin', permissions: ['read', 'write'] }) }; }
const user = { id: 1, name: 'Alice', profile: { age: 25 } }; const updatedUser = { ...user, profile: { ...user.profile, age: 26, city: 'Shanghai' } };
const { password, ...publicUser } = user;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| function greet(first, second, third) { return `Hello ${first}, ${second}, and ${third}!`; }
const names = ['Alice', 'Bob', 'Charlie']; console.log(greet(...names));
function sum(first, ...rest) { console.log('第一个:', first); console.log('其余的:', rest); return first + rest.reduce((a, b) => a + b, 0); }
sum(1, 2, 3, 4, 5);
function middleware(fn) { return function(...args) { console.log('调用前处理'); const result = fn(...args); console.log('调用后处理'); return result; }; }
function movePlayer(player, ...movements) { return movements.reduce((pos, [dx, dy]) => ({ x: pos.x + dx, y: pos.y + dy }), player); }
const player = { x: 0, y: 0 }; const newPos = movePlayer(player, [1, 0], [0, 1], [-1, 0]);
|
展开运算符的超能力:
- 🔗 数组合并:比concat更直观
- 📋 浅拷贝:避免引用问题
- 🎯 参数展开:数组转参数列表
- 🏗️ 对象合并:配置覆盖的利器
- 🔄 不可变更新:函数式编程必备
五、可选链与空值合并:告别防御性编程 🛡️
还在写一堆if判断来防止报错?还在用||来设置默认值却被0和false坑?可选链和空值合并操作符来拯救你!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const user = getUser(); let city; if (user && user.profile && user.profile.address && user.profile.address.city) { city = user.profile.address.city; } else { city = '未知'; }
const city = user?.profile?.address?.city ?? '未知';
const response = await fetch('/api/user'); const data = await response.json();
const userName = data?.user?.profile?.name ?? '匿名用户'; const avatarUrl = data?.user?.profile?.avatar ?? '/default-avatar.png'; const permissions = data?.user?.roles?.[0]?.permissions ?? [];
const button = document.querySelector('#submit-btn'); button?.addEventListener?.('click', handleClick);
const player = game?.state?.currentLevel?.player; const health = player?.stats?.health ?? 100; const weapon = player?.inventory?.weapons?.[0]?.name ?? '拳头';
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const config = { theme: '', count: 0, enabled: false, timeout: null };
const theme = config.theme || 'default'; const count = config.count || 10; const enabled = config.enabled || true;
const theme2 = config.theme ?? 'default'; const count2 = config.count ?? 10; const enabled2 = config.enabled ?? true; const timeout = config.timeout ?? 5000;
function applyUserSettings(userSettings) { const settings = { theme: userSettings?.theme ?? 'auto', fontSize: userSettings?.fontSize ?? 14, notifications: userSettings?.notifications ?? true, autoSave: userSettings?.autoSave ?? false, language: userSettings?.language ?? navigator.language }; return settings; }
function calculateScore(data) { const baseScore = data?.score ?? 0; const bonus = data?.achievements?.length ?? 0; const penalty = data?.violations?.count ?? 0; return Math.max(0, baseScore + bonus * 10 - penalty * 5); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| function UserProfile({ user }) { return ( <div className="user-profile"> <img src={user?.avatar ?? '/default-avatar.png'} alt={user?.name ?? '用户'} /> <h2>{user?.name ?? '匿名用户'}</h2> <p>邮箱: {user?.email ?? '未设置'}</p> <p>积分: {user?.points?.toLocaleString?.() ?? '0'}</p> {/* 条件渲染也更安全 */} {user?.isVip && <span className="vip-badge">VIP</span>} {/* 嵌套组件传递props */} <ContactInfo phone={user?.contact?.phone} address={user?.contact?.address?.street} /> </div> ); }
async function fetchUserData(userId) { try { const response = await fetch(`/api/users/${userId}`); const data = await response.json(); return { id: data?.id ?? userId, name: data?.profile?.name ?? '未知用户', email: data?.contact?.email ?? null, lastLogin: data?.activity?.lastLogin ?? new Date(), preferences: { theme: data?.settings?.theme ?? 'light', language: data?.settings?.language ?? 'zh-CN', notifications: data?.settings?.notifications ?? true } }; } catch (error) { console.error('获取用户数据失败:', error); return null; } }
function validateForm(formData) { const errors = {}; if (!formData?.name?.trim?.()) { errors.name = '姓名不能为空'; } if (!formData?.email?.includes?.('@')) { errors.email = '邮箱格式不正确'; } if ((formData?.age ?? 0) < 18) { errors.age = '年龄必须大于18岁'; } return Object.keys(errors).length === 0 ? null : errors; }
|
注意区别:
?. 可选链:安全访问属性/方法,遇到null或undefined就停止
?? 空值合并:只有null和undefined才使用默认值
|| 逻辑或:所有假值(false、0、''、null、undefined)都使用默认值
最佳实践:
- 🛡️ API数据访问必用可选链
- 🎯 配置默认值优选空值合并
- 🚀 减少防御性代码,提高可读性
- ⚡ 性能友好:短路求值机制
七、箭头函数:不只是语法糖 🏹
箭头函数不仅仅让代码更简洁,更重要的是它改变了this的绑定规则,解决了传统函数中this指向混乱的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| class Timer { constructor() { this.seconds = 0; this.element = document.getElementById('timer'); } start() { const self = this; setInterval(function() { self.seconds++; self.element.textContent = self.seconds; }, 1000); setInterval(function() { this.seconds++; this.element.textContent = this.seconds; }.bind(this), 1000); } }
class ModernTimer { constructor() { this.seconds = 0; this.element = document.getElementById('timer'); } start() { setInterval(() => { this.seconds++; this.element.textContent = this.seconds; }, 1000); } bindEvents() { this.element.addEventListener('click', () => { this.reset(); }); ['mouseenter', 'mouseleave'].forEach(event => { this.element.addEventListener(event, () => { this.handleHover(event); }); }); } }
class GameComponent extends React.Component { constructor(props) { super(props); this.state = { score: 0, level: 1 }; } handleScoreUpdate = (points) => { this.setState(prevState => ({ score: prevState.score + points })); } handleLevelUp = () => { this.setState(prevState => ({ level: prevState.level + 1, score: 0 })); } render() { return ( <div> <button onClick={this.handleScoreUpdate.bind(null, 10)}> +10分 </button> <button onClick={this.handleLevelUp}> 升级 </button> </div> ); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(n) { return n * 2; }); const doubledArrow = numbers.map(n => n * 2);
const evens = numbers.filter(function(n) { return n % 2 === 0; }); const evensArrow = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce(function(acc, n) { return acc + n; }, 0); const sumArrow = numbers.reduce((acc, n) => acc + n, 0);
const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(n => n % 2 === 0) .map(n => n * n) .filter(n => n > 10) .reduce((sum, n) => sum + n, 0);
const processUserData = (users) => users .filter(user => user.isActive) .map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}`, age: new Date().getFullYear() - user.birthYear })) .sort((a, b) => b.age - a.age) .slice(0, 10);
const createValidator = (rule) => (value) => rule(value); const createFormatter = (format) => (value) => format(value);
const isEmail = createValidator(email => /\S+@\S+\.\S+/.test(email)); const formatCurrency = createFormatter(amount => `¥${amount.toFixed(2)}`);
const emails = ['test@example.com', 'invalid-email', 'user@domain.org']; const validEmails = emails.filter(isEmail);
const prices = [99.9, 199.99, 299]; const formattedPrices = prices.map(formatCurrency);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
const Person = (name) => { this.name = name; };
function traditionalFunc() { console.log(arguments); }
const arrowFunc = () => { console.log(arguments); };
const arrowFuncWithRest = (...args) => { console.log(args); };
const obj = { name: 'Alice', sayHello: () => { console.log(`Hello, ${this.name}`); }, sayHi: function() { console.log(`Hi, ${this.name}`); }, sayGoodbye() { console.log(`Goodbye, ${this.name}`); } };
function User(name) { this.name = name; }
User.prototype.getName = () => { return this.name; };
User.prototype.getName = function() { return this.name; };
|
箭头函数使用原则:
- ✅ 回调函数:事件处理、数组方法、定时器
- ✅ 函数式编程:map、filter、reduce等
- ✅ 需要绑定外层this:类方法、嵌套函数
- ❌ 对象方法:需要动态this时
- ❌ 构造函数:需要new调用时
- ❌ 原型方法:需要实例this时
八、模块化:告别全局变量污染 📦
ES6模块化是现代JavaScript开发的基石,它让代码组织更清晰,依赖关系更明确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
export const PI = 3.14159; export const E = 2.71828;
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }
const subtract = (a, b) => a - b; const divide = (a, b) => a / b; export { subtract, divide };
const power = (base, exponent) => Math.pow(base, exponent); export { power as pow };
export default class Calculator { constructor() { this.history = []; } calculate(operation, a, b) { const result = operation(a, b); this.history.push({ operation: operation.name, a, b, result }); return result; } }
import Calculator from './utils/math.js';
import { add, multiply, PI } from './utils/math.js';
import { pow as power } from './utils/math.js';
import * as MathUtils from './utils/math.js';
import Calculator, { add, PI } from './utils/math.js';
const calc = new Calculator(); const result = calc.calculate(add, 5, 3); console.log(`5 + 3 = ${result}`); console.log(`π = ${PI}`);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
async function loadFeature(featureName) { if (featureName === 'chart') { const { Chart } = await import('./features/chart.js'); return new Chart(); } else if (featureName === 'editor') { const { Editor } = await import('./features/editor.js'); return new Editor(); } }
const routes = [ { path: '/dashboard', component: () => import('./pages/Dashboard.js') }, { path: '/profile', component: () => import('./pages/Profile.js') } ];
async function loadRoute(path) { const route = routes.find(r => r.path === path); if (route) { const module = await route.component(); return module.default; } }
class PluginManager { constructor() { this.plugins = new Map(); } async loadPlugin(pluginName) { if (this.plugins.has(pluginName)) { return this.plugins.get(pluginName); } try { const module = await import(`./plugins/${pluginName}.js`); const plugin = new module.default(); this.plugins.set(pluginName, plugin); return plugin; } catch (error) { console.error(`加载插件 ${pluginName} 失败:`, error); return null; } } async executePlugin(pluginName, ...args) { const plugin = await this.loadPlugin(pluginName); return plugin?.execute(...args); } }
class I18n { constructor() { this.messages = new Map(); this.currentLocale = 'zh-CN'; } async loadLocale(locale) { if (this.messages.has(locale)) { return this.messages.get(locale); } try { const module = await import(`./locales/${locale}.js`); this.messages.set(locale, module.default); return module.default; } catch (error) { console.warn(`加载语言包 ${locale} 失败,使用默认语言`); return this.messages.get('zh-CN') || {}; } } async setLocale(locale) { await this.loadLocale(locale); this.currentLocale = locale; } async t(key) { const messages = await this.loadLocale(this.currentLocale); return messages[key] || key; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
class ApiService { constructor(baseURL = '/api') { this.baseURL = baseURL; this.interceptors = { request: [], response: [] }; } addRequestInterceptor(interceptor) { this.interceptors.request.push(interceptor); } addResponseInterceptor(interceptor) { this.interceptors.response.push(interceptor); } async request(endpoint, options = {}) { let config = { ...options }; for (const interceptor of this.interceptors.request) { config = await interceptor(config); } let response = await fetch(`${this.baseURL}${endpoint}`, config); for (const interceptor of this.interceptors.response) { response = await interceptor(response); } return response; } }
const apiService = new ApiService();
apiService.addRequestInterceptor(async (config) => { const token = localStorage.getItem('token'); if (token) { config.headers = { ...config.headers, 'Authorization': `Bearer ${token}` }; } return config; });
export default apiService; export { ApiService };
class UserStore { constructor() { this.state = { user: null, isLoading: false, error: null }; this.listeners = []; } subscribe(listener) { this.listeners.push(listener); return () => { const index = this.listeners.indexOf(listener); if (index > -1) { this.listeners.splice(index, 1); } }; } setState(newState) { this.state = { ...this.state, ...newState }; this.listeners.forEach(listener => listener(this.state)); } async login(credentials) { this.setState({ isLoading: true, error: null }); try { const response = await apiService.request('/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials) }); const data = await response.json(); if (response.ok) { localStorage.setItem('token', data.token); this.setState({ user: data.user, isLoading: false }); } else { throw new Error(data.message); } } catch (error) { this.setState({ error: error.message, isLoading: false }); } } logout() { localStorage.removeItem('token'); this.setState({ user: null, error: null }); } }
export default new UserStore();
import userStore from '../stores/userStore.js';
export default class UserProfile { constructor(container) { this.container = container; this.unsubscribe = null; this.init(); } init() { this.unsubscribe = userStore.subscribe((state) => { this.render(state); }); this.render(userStore.state); } render(state) { if (state.isLoading) { this.container.innerHTML = '<div>加载中...</div>'; return; } if (state.error) { this.container.innerHTML = `<div class="error">${state.error}</div>`; return; } if (state.user) { this.container.innerHTML = ` <div class="user-profile"> <h2>欢迎, ${state.user.name}!</h2> <p>邮箱: ${state.user.email}</p> <button id="logout-btn">退出登录</button> </div> `; this.container.querySelector('#logout-btn') .addEventListener('click', () => userStore.logout()); } else { this.container.innerHTML = '<div>请先登录</div>'; } } destroy() { if (this.unsubscribe) { this.unsubscribe(); } } }
|
模块化最佳实践:
- 🎯 单一职责:每个模块只做一件事
- 📦 明确接口:清晰的导入导出
- 🔄 循环依赖:避免模块间循环引用
- ⚡ 按需加载:使用动态导入优化性能
- 🛡️ 错误处理:模块加载失败的降级方案
六、异步编程革命:从回调地狱到Promise天堂 🚀
异步编程是JavaScript的灵魂,从回调地狱到Promise,再到async/await,每一步都是质的飞跃!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| function getUserData(userId, callback) { getUser(userId, (err, user) => { if (err) { callback(err); return; } getProfile(user.id, (err, profile) => { if (err) { callback(err); return; } getPreferences(profile.id, (err, preferences) => { if (err) { callback(err); return; } getFriends(user.id, (err, friends) => { if (err) { callback(err); return; } callback(null, { user, profile, preferences, friends }); }); }); }); }); }
getUserData(123, (err, data) => { if (err) { console.error('获取用户数据失败:', err); return; } console.log('用户数据:', data); });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| function getUserData(userId) { return getUser(userId) .then(user => { return Promise.all([ Promise.resolve(user), getProfile(user.id), getPreferences(user.id), getFriends(user.id) ]); }) .then(([user, profile, preferences, friends]) => ({ user, profile, preferences, friends })) .catch(err => { console.error('获取用户数据失败:', err); throw err; }); }
async function handleMultipleRequests() { try { const [users, posts, comments] = await Promise.all([ fetchUsers(), fetchPosts(), fetchComments() ]); const fastestResponse = await Promise.race([ fetchFromCDN(), fetchFromBackup(), fetchFromCache() ]); const results = await Promise.allSettled([ riskyOperation1(), riskyOperation2(), riskyOperation3() ]); const successful = results .filter(result => result.status === 'fulfilled') .map(result => result.value); return { users, posts, comments, fastestResponse, successful }; } catch (error) { console.error('批量操作失败:', error); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| async function getUserData(userId) { try { const user = await getUser(userId); const profilePromise = getProfile(user.id); const preferencesPromise = getPreferences(user.id); const friendsPromise = getFriends(user.id); const [profile, preferences, friends] = await Promise.all([ profilePromise, preferencesPromise, friendsPromise ]); return { user, profile, preferences, friends }; } catch (error) { console.error('获取用户数据失败:', error); if (error.code === 'USER_NOT_FOUND') { return null; } throw error; } }
async function loadGameData(gameId) { const loadingSteps = [ { name: '加载游戏配置', fn: () => loadConfig(gameId) }, { name: '加载关卡数据', fn: () => loadLevels(gameId) }, { name: '加载角色数据', fn: () => loadCharacters(gameId) }, { name: '加载音效资源', fn: () => loadAudio(gameId) }, { name: '加载图片资源', fn: () => loadImages(gameId) } ]; const results = {}; for (const step of loadingSteps) { try { console.log(`开始${step.name}...`); const startTime = Date.now(); results[step.name] = await step.fn(); const duration = Date.now() - startTime; console.log(`${step.name}完成,耗时${duration}ms`); updateLoadingProgress(step.name); } catch (error) { console.error(`${step.name}失败:`, error); if (step.name === '加载游戏配置') { throw new Error('游戏配置加载失败,无法继续'); } } } return results; }
class ApiClient { constructor(baseURL, timeout = 5000) { this.baseURL = baseURL; this.timeout = timeout; } async request(endpoint, options = {}) { const url = `${this.baseURL}${endpoint}`; const config = { timeout: this.timeout, headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }; try { const response = await fetch(url, config); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return data; } catch (error) { if (error.name === 'AbortError') { throw new Error('请求超时'); } throw error; } } async get(endpoint, params = {}) { const query = new URLSearchParams(params).toString(); const url = query ? `${endpoint}?${query}` : endpoint; return this.request(url); } async post(endpoint, data) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) }); } }
|
异步编程最佳实践:
- 🎯 优先使用async/await:代码更直观易读
- ⚡ 合理使用Promise.all:并行执行提高性能
- 🛡️ 错误处理要完善:try/catch + 错误分类
- 🔄 避免在循环中await:除非需要串行执行
- 📊 监控异步操作:超时、重试、进度反馈
特点:层层嵌套,错误处理复杂
问题:回调地狱,代码难以维护
适用:简单的异步操作
特点:链式调用,统一错误处理
优势:解决回调地狱,支持并行操作
工具:Promise.all、Promise.race等
特点:同步风格写异步代码
优势:代码直观,错误处理简单
现状:现代JavaScript的标准写法
九、事件委托 🎯
事件委托是 JavaScript 中一个经典的模式,它通过利用事件冒泡机制,让我们可以更高效地处理事件。
1 2 3 4 5 6
| document.querySelector('#list').addEventListener('click', e => { if(e.target.matches('li.item')) { e.target.classList.toggle('active'); } });
|
事件委托的优势
事件委托可以减少事件监听器的数量,提高性能。同时,它还能处理动态生成的元素。
十、作用域与闭包 🔄
闭包是 JavaScript 中一个重要的概念,它让我们可以创建私有的作用域,保护数据不受外界干扰。
1 2 3 4 5 6 7
| function createCounter() { let count = 0; return () => ++count; } const counter = createCounter(); console.log(counter());
|
闭包的用途
闭包可以用来创建私有变量和方法,避免数据被意外修改。它在实现模块化和封装时非常有用。
一、箭头函数的 this 绑定规则 🔄
箭头函数是 ES6 中对函数的一个重要改进,它的 this 绑定规则与传统函数不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const obj = { name: "Alice", say: function() { console.log(this.name); } };
const obj2 = { name: "Bob", say: () => { console.log(this.name); } };
class Button { constructor() { this.text = "Click me"; this.element.addEventListener('click', () => { console.log(this.text); }); } }
|
箭头函数的优缺点
箭头函数的词法作用域特性,让我们在回调函数中可以轻松地保留外层的 this。但它也有一些限制,比如不能作为构造函数。
二、模块化(import/export) 📦
模块化是 ES6 中一个重要的特性,它让我们可以更好地组织代码,实现模块化开发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
export const PI = 3.14159; export function sum(a, b) { return a + b; }
export default class Calculator { }
import Calculator, { PI, sum } from './math.js'; import * as MathUtils from './math.js';
const loadModule = async () => { const module = await import('./math.js'); console.log(module.PI); };
|
模块化的优势
模块化让代码更加模块化和可维护。通过 import 和 export,我们可以清晰地定义模块的接口,避免全局变量的污染。
三、Proxy/Reflect 元编程 🔄
Proxy 和 Reflect 是 ES6 中对元编程的支持,它们让我们可以更深入地控制对象的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const target = { name: "Alice" }; const handler = { get(obj, prop) { console.log(`访问属性: ${prop}`); return Reflect.get(...arguments); }, set(obj, prop, value) { if (prop === 'age' && value < 0) { throw new Error("年龄不能为负"); } return Reflect.set(...arguments); } };
const proxy = new Proxy(target, handler); proxy.name; proxy.age = 25; proxy.age = -5;
|
Proxy 和 Reflect 的用途
Proxy 可以用来拦截和自定义对象的操作,而 Reflect 提供了默认的操作实现。它们在实现高级模式时非常有用。
四、Map/Set 数据结构 🔄
Map 和 Set 是 ES6 中新增的数据结构,它们提供了更加灵活的方式来存储和操作数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const map = new Map(); map.set(1, "数值键"); map.set({id: 1}, "对象键");
for (const [key, value] of map) { console.log(`${key}: ${value}`); }
const uniqueNumbers = new Set([1, 2, 2, 3]); uniqueNumbers.add(4).delete(1);
const hasTwo = uniqueNumbers.has(2); const size = uniqueNumbers.size;
|
Map 和 Set 的优势
Map 和 Set 提供了更加灵活的方式来存储和操作数据。Map 支持任意类型的键,而 Set 自动去重,这些特性在处理复杂数据时非常有用。
五、迭代器与生成器 🔄
迭代器和生成器是 ES6 中对迭代协议的支持,它们让我们可以更灵活地处理数据流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const customIterable = { [Symbol.iterator]() { let step = 0; return { next() { return step < 3 ? { value: step++, done: false } : { done: true }; } }; } };
function* idGenerator() { let id = 1; while (true) { yield id++; } }
const gen = idGenerator(); console.log(gen.next().value); console.log(gen.next().value);
|
迭代器和生成器的用途
迭代器和生成器让我们可以更灵活地处理数据流。生成器尤其适合处理惰性求值和复杂状态流。
九、Map与Set:数据结构的新选择 🗺️
传统的Object和Array已经不能满足所有需求?Map和Set为我们提供了更强大、更灵活的数据结构选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
const obj = {}; obj[1] = 'number key'; obj[true] = 'boolean key'; obj[{}] = 'object key';
console.log(Object.keys(obj));
const map = new Map();
map.set(1, 'number key'); map.set('1', 'string key'); map.set(true, 'boolean key'); map.set({id: 1}, 'object key'); map.set(() => {}, 'function key');
console.log(map.size);
class Cache { constructor(maxSize = 100) { this.cache = new Map(); this.maxSize = maxSize; } get(key) { if (this.cache.has(key)) { const value = this.cache.get(key); this.cache.delete(key); this.cache.set(key, value); return value; } return null; } set(key, value) { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.maxSize) { const firstKey = this.cache.keys().next().value; this.cache.delete(firstKey); } this.cache.set(key, value); } clear() { this.cache.clear(); } getStats() { return { size: this.cache.size, maxSize: this.maxSize, keys: Array.from(this.cache.keys()) }; } }
const nodeData = new Map();
function attachData(element, data) { nodeData.set(element, data); }
function getData(element) { return nodeData.get(element); }
const button = document.querySelector('#my-button'); attachData(button, { clickCount: 0, lastClicked: null, handler: () => console.log('clicked!') });
class Counter { constructor() { this.counts = new Map(); } increment(key) { this.counts.set(key, (this.counts.get(key) || 0) + 1); } getCount(key) { return this.counts.get(key) || 0; } getTop(n = 10) { return Array.from(this.counts.entries()) .sort(([,a], [,b]) => b - a) .slice(0, n); } reset() { this.counts.clear(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
const numbers = [1, 2, 2, 3, 3, 4, 5, 5]; const uniqueNumbers = [...new Set(numbers)];
const text = "hello world"; const uniqueChars = [...new Set(text)];
function arrayIntersection(arr1, arr2) { const set1 = new Set(arr1); return arr2.filter(item => set1.has(item)); }
function arrayUnion(arr1, arr2) { return [...new Set([...arr1, ...arr2])]; }
function arrayDifference(arr1, arr2) { const set2 = new Set(arr2); return arr1.filter(item => !set2.has(item)); }
const fruits1 = ['apple', 'banana', 'orange']; const fruits2 = ['banana', 'orange', 'grape'];
console.log(arrayIntersection(fruits1, fruits2)); console.log(arrayUnion(fruits1, fruits2)); console.log(arrayDifference(fruits1, fruits2));
class TagSystem { constructor() { this.entityTags = new Map(); this.tagEntities = new Map(); } addTag(entityId, tag) { if (!this.entityTags.has(entityId)) { this.entityTags.set(entityId, new Set()); } this.entityTags.get(entityId).add(tag); if (!this.tagEntities.has(tag)) { this.tagEntities.set(tag, new Set()); } this.tagEntities.get(tag).add(entityId); } removeTag(entityId, tag) { this.entityTags.get(entityId)?.delete(tag); this.tagEntities.get(tag)?.delete(entityId); } hasTag(entityId, tag) { return this.entityTags.get(entityId)?.has(tag) || false; } getEntitiesByTag(tag) { return Array.from(this.tagEntities.get(tag) || []); } getEntityTags(entityId) { return Array.from(this.entityTags.get(entityId) || []); } getEntitiesWithAllTags(...tags) { if (tags.length === 0) return []; let result = this.getEntitiesByTag(tags[0]); for (let i = 1; i < tags.length; i++) { const entitiesWithTag = this.getEntitiesByTag(tags[i]); result = result.filter(id => entitiesWithTag.includes(id)); } return result; } }
const tagSystem = new TagSystem();
tagSystem.addTag('player1', 'human'); tagSystem.addTag('player1', 'warrior'); tagSystem.addTag('player1', 'alive');
tagSystem.addTag('enemy1', 'orc'); tagSystem.addTag('enemy1', 'warrior'); tagSystem.addTag('enemy1', 'alive');
console.log(tagSystem.getEntitiesByTag('warrior')); console.log(tagSystem.getEntitiesWithAllTags('warrior', 'alive'));
class PermissionSystem { constructor() { this.userPermissions = new Map(); this.rolePermissions = new Map(); this.userRoles = new Map(); } addPermissionToRole(role, permission) { if (!this.rolePermissions.has(role)) { this.rolePermissions.set(role, new Set()); } this.rolePermissions.get(role).add(permission); } assignRoleToUser(userId, role) { if (!this.userRoles.has(userId)) { this.userRoles.set(userId, new Set()); } this.userRoles.get(userId).add(role); } getUserPermissions(userId) { const permissions = new Set(); const directPermissions = this.userPermissions.get(userId) || new Set(); directPermissions.forEach(p => permissions.add(p)); const roles = this.userRoles.get(userId) || new Set(); roles.forEach(role => { const rolePerms = this.rolePermissions.get(role) || new Set(); rolePerms.forEach(p => permissions.add(p)); }); return permissions; } hasPermission(userId, permission) { return this.getUserPermissions(userId).has(permission); } }
|
Map vs Object,Set vs Array:
- 🗺️ Map优势:任意类型键、有序、size属性、更好的迭代性能
- 📦 Object优势:JSON序列化、属性访问语法、更好的内存效率(小数据)
- 🎯 Set优势:自动去重、has()方法O(1)复杂度、数学集合操作
- 📋 Array优势:索引访问、丰富的方法、JSON序列化
🎯 ES6学习路线图与实战建议
学完这么多特性,如何在实际项目中应用?这里给出一个循序渐进的学习路线:
掌握核心语法
- ✅ let/const替代var
- ✅ 箭头函数基本用法
- ✅ 模板字符串
- ✅ 解构赋值
- ✅ 展开运算符
实践项目:重构一个小型项目,用ES6语法替换ES5写法
异步编程精通
- ✅ Promise深入理解
- ✅ async/await熟练运用
- ✅ 错误处理最佳实践
- ✅ 并发控制技巧
实践项目:构建一个数据获取和处理的应用
模块化与架构
- ✅ ES6模块系统
- ✅ 动态导入
- ✅ 模块设计模式
- ✅ 打包工具配置
实践项目:设计一个可扩展的前端架构
深度特性应用
- ✅ Map/Set数据结构
- ✅ Proxy/Reflect元编程
- ✅ 迭代器与生成器
- ✅ 性能优化技巧
实践项目:开发一个复杂的业务系统或开源库
🚀 总结:拥抱现代JavaScript
ES6不仅仅是语法的升级,更是编程思维的转变。它让JavaScript从一个”玩具语言”真正成长为企业级开发语言。
🎯 核心收获
- 语法更简洁:箭头函数、模板字符串、解构赋值让代码更优雅
- 功能更强大:Promise、模块化、新数据结构解决实际问题
- 开发更高效:可选链、空值合并减少防御性编程
- 架构更清晰:模块化让大型项目组织更合理
💡 实践建议
- 循序渐进:从基础语法开始,逐步掌握高级特性
- 项目驱动:在实际项目中应用,加深理解
- 持续学习:JavaScript生态快速发展,保持学习热情
- 分享交流:与同行交流经验,共同进步
⚠️ 注意事项
- 浏览器兼容性:考虑目标用户的浏览器支持情况
- 打包工具:使用Babel等工具确保兼容性
- 性能考量:新特性虽好,但要考虑性能影响
- 团队规范:制定团队编码规范,保持代码一致性
现代JavaScript的世界精彩纷呈,ES6只是一个开始。继续探索ES2017、ES2018…的新特性,让我们的代码更加现代化!
Happy Coding! 🎉