一、Promise与异步编程
1. 深入理解Promise原理及使用
Promise三种状态:
pending(待定):初始状态fulfilled(已兑现):操作成功rejected(已拒绝):操作失败
javascript
// 基本使用
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('操作成功');
} else {
reject('操作失败');
}
}, 1000);
});
promise
.then(result => {
console.log(result);
return '链式调用';
})
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('无论成功失败都执行');
});
Promise链式调用规则:
javascript
// 1. then返回新的Promise
Promise.resolve(1)
.then(value => {
console.log(value); // 1
return value + 1;
})
.then(value => {
console.log(value); // 2
return Promise.resolve(value + 1);
})
.then(value => {
console.log(value); // 3
});
// 2. 错误传播
Promise.reject('错误')
.then(value => {
console.log('不会执行');
})
.then(value => {
console.log('也不会执行');
})
.catch(error => {
console.error(error); // '错误'
return '恢复正常';
})
.then(value => {
console.log(value); // '恢复正常'
});
// 3. 错误处理最佳实践
function fetchData(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
console.error('Fetch error:', error);
throw error; // 重新抛出,让调用者处理
});
}
2. Promise静态方法详解
javascript
// 1. Promise.all - 全部成功才成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);
Promise.all([p1, p2, p3])
.then(results => {
console.log(results); // [1, 2, 3]
})
.catch(error => {
console.error('有一个失败');
});
// 应用场景:并发请求
async function fetchMultipleData() {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
return { users, posts, comments };
}
// 2. Promise.race - 第一个完成的结果
Promise.race([
new Promise(resolve => setTimeout(() => resolve('快'), 100)),
new Promise(resolve => setTimeout(() => resolve('慢'), 500))
]).then(result => {
console.log(result); // '快'
});
// 应用场景:请求超时
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
// 3. Promise.allSettled - 等待所有Promise完成(无论成功失败)
Promise.allSettled([
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3)
]).then(results => {
console.log(results);
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: 'error' },
// { status: 'fulfilled', value: 3 }
// ]
});
// 4. Promise.any - 第一个成功的结果
Promise.any([
Promise.reject('错误1'),
Promise.resolve('成功'),
Promise.reject('错误2')
]).then(result => {
console.log(result); // '成功'
});
3. 手写实现Promise
javascript
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
return promise2;
}
resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason })
);
}
static resolve(value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
static all(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
let count = 0;
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
count++;
if (count === promises.length) {
resolve(results);
}
},
reject
);
});
});
}
}
4. async/await深度解析
基本概念:
async函数返回Promiseawait等待Promise完成- 更优雅的异步代码书写方式
javascript
// 1. 基本使用
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
return user;
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// 2. 错误处理
async function multipleRequests() {
try {
const user = await fetchUserData(1);
const posts = await fetch(`/api/users/${user.id}/posts`).then(r => r.json());
return { user, posts };
} catch (error) {
// 统一错误处理
console.error(error);
}
}
// 3. 并发执行
async function concurrentRequests() {
// ❌ 串行执行(慢)
const user1 = await fetchUserData(1);
const user2 = await fetchUserData(2);
const user3 = await fetchUserData(3);
// ✅ 并行执行(快)
const [user1, user2, user3] = await Promise.all([
fetchUserData(1),
fetchUserData(2),
fetchUserData(3)
]);
return [user1, user2, user3];
}
// 4. 循环中使用await
async function processUsers(userIds) {
// ❌ forEach不会等待
userIds.forEach(async (id) => {
const user = await fetchUserData(id);
console.log(user);
});
// ✅ 串行处理
for (const id of userIds) {
const user = await fetchUserData(id);
console.log(user);
}
// ✅ 并行处理
const users = await Promise.all(
userIds.map(id => fetchUserData(id))
);
return users;
}
// 5. 实现sleep函数
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function demo() {
console.log('开始');
await sleep(2000);
console.log('2秒后');
}
async/await vs Promise对比:
javascript
// Promise方式
function getUserPosts() {
return fetchUserData(1)
.then(user => fetch(`/api/users/${user.id}/posts`))
.then(response => response.json())
.then(posts => {
console.log(posts);
return posts;
})
.catch(error => {
console.error(error);
});
}
// async/await方式(更清晰)
async function getUserPosts() {
try {
const user = await fetchUserData(1);
const response = await fetch(`/api/users/${user.id}/posts`);
const posts = await response.json();
console.log(posts);
return posts;
} catch (error) {
console.error(error);
}
}
二、解构赋值与展开运算符
5. 解构赋值的高级用法
javascript
// 1. 数组解构
const [a, b, c] = [1, 2, 3];
const [first, , third] = [1, 2, 3]; // 跳过元素
const [x, ...rest] = [1, 2, 3, 4]; // rest: [2, 3, 4]
const [m, n, o = 0] = [1, 2]; // 默认值
// 交换变量
let p = 1, q = 2;
[p, q] = [q, p];
// 2. 对象解构
const { name, age } = { name: 'Alice', age: 18 };
const { name: userName, age: userAge } = { name: 'Bob', age: 20 }; // 重命名
const { city = 'Beijing' } = {}; // 默认值
// 嵌套解构
const user = {
id: 1,
info: {
name: 'Charlie',
address: {
city: 'Shanghai'
}
}
};
const { info: { name, address: { city } } } = user;
// 3. 函数参数解构
function createUser({ name, age = 18, ...others }) {
return { name, age, ...others };
}
createUser({ name: 'David', gender: 'male' });
// 4. 动态属性名解构
const key = 'username';
const { [key]: value } = { username: 'admin' };
console.log(value); // 'admin'
// 5. 实际应用场景
// React Hooks
const [count, setCount] = useState(0);
// API响应处理
const { data: { users, total }, status } = await axios.get('/api/users');
// 配置对象
function request({ url, method = 'GET', headers = {}, data }) {
// ...
}
6. 展开运算符的应用场景
javascript
// 1. 数组操作
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 数组拷贝
const copied = [...arr1]; // 浅拷贝
// 数组去重
const unique = [...new Set([1, 2, 2, 3, 3])]; // [1, 2, 3]
// 找最大值
const max = Math.max(...[1, 5, 3, 9, 2]); // 9
// 2. 对象操作
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
// 合并对象(后面的覆盖前面的)
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
// 对象拷贝
const copied = { ...obj1 };
// 添加/修改属性
const updated = { ...obj1, b: 10, d: 5 };
// 3. 函数参数
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
sum(1, 2, 3, 4); // 10
// 4. React实际应用
// 状态更新
setState(prevState => ({
...prevState,
count: prevState.count + 1
}));
// Props传递
const props = { name: 'Alice', age: 18 };
<Component {...props} />
// 5. 条件属性
const isAdmin = true;
const user = {
name: 'Bob',
...(isAdmin && { role: 'admin' })
};
三、箭头函数与函数增强
7. 箭头函数的特性与限制
核心特性:
javascript
// 1. this绑定
const obj = {
name: 'Alice',
// 普通函数:this动态绑定
sayName1: function() {
setTimeout(function() {
console.log(this.name); // undefined,this指向window
}, 100);
},
// 箭头函数:this继承外层
sayName2: function() {
setTimeout(() => {
console.log(this.name); // 'Alice',this指向obj
}, 100);
}
};
// 2. 没有arguments对象
const normalFunc = function() {
console.log(arguments); // ✅ 可用
};
const arrowFunc = () => {
console.log(arguments); // ❌ ReferenceError
};
// 使用剩余参数代替
const arrowFunc2 = (...args) => {
console.log(args); // ✅ 可用
};
// 3. 不能作为构造函数
const Person = (name) => {
this.name = name;
};
// new Person('Alice'); // ❌ TypeError
// 4. 没有prototype属性
console.log(arrowFunc.prototype); // undefined
// 5. 不能用作Generator函数
// const gen = *() => {}; // ❌ 语法错误
何时使用箭头函数:
✅ 适合使用:
- 需要继承外层this的场景
- 简短的函数表达式
- 数组方法的回调
- Promise链中的回调
❌ 不适合使用:
- 对象方法(需要动态this)
- 需要使用arguments
- 需要作为构造函数
- 需要动态绑定this的事件处理器
javascript
// ❌ 对象方法不要用箭头函数
const calculator = {
value: 0,
add: (num) => {
this.value += num; // this不指向calculator
}
};
// ✅ 使用普通函数
const calculator = {
value: 0,
add(num) {
this.value += num;
}
};
8. 函数默认参数与剩余参数
javascript
// 1. 默认参数
function greet(name = 'Guest', greeting = 'Hello') {
return `${greeting}, ${name}!`;
}
greet(); // 'Hello, Guest!'
greet('Alice'); // 'Hello, Alice!'
greet('Bob', 'Hi'); // 'Hi, Bob!'
// 默认参数表达式
function createId(prefix = 'user', id = Date.now()) {
return `${prefix}_${id}`;
}
// 参数默认值可以引用其他参数
function multiply(a, b = a * 2) {
return a * b;
}
multiply(5); // 50
// 2. 剩余参数
function sum(first, ...rest) {
console.log(first); // 第一个参数
console.log(rest); // 其余参数数组
return rest.reduce((acc, num) => acc + num, first);
}
sum(1, 2, 3, 4, 5); // 15
// 3. 实际应用
// 日志函数
function log(level, ...messages) {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] [${level}]`, ...messages);
}
log('INFO', 'Server started', 'Port: 3000');
// 组合多个函数
function compose(...fns) {
return (value) => fns.reduceRight((acc, fn) => fn(acc), value);
}
const add1 = x => x + 1;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;
const combined = compose(subtract3, multiply2, add1);
combined(5); // (5 + 1) * 2 - 3 = 9
四、模块化
9. ES6模块与CommonJS的区别
ES6模块(ESM):
javascript
// math.js - 导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default class Calculator {
// ...
}
// app.js - 导入
import Calculator, { PI, add } from './math.js';
import * as math from './math.js';
import { add as sum } from './math.js'; // 重命名
CommonJS:
javascript
// math.js - 导出
const PI = 3.14159;
function add(a, b) {
return a + b;
}
module.exports = { PI, add };
// 或
exports.PI = PI;
exports.add = add;
// app.js - 导入
const { PI, add } = require('./math.js');
const math = require('./math.js');
核心区别:
| 特性 | ES6模块 | CommonJS |
|---|---|---|
| 加载方式 | 编译时加载(静态) | 运行时加载(动态) |
| 输出 | 值的引用 | 值的拷贝 |
| this指向 | undefined | 当前模块 |
| 循环依赖 | 支持 | 有限支持 |
| Tree Shaking | 支持 | 不支持 |
| 使用场景 | 浏览器、现代Node.js | Node.js |
javascript
// ES6模块 - 值的引用
// counter.js
export let count = 0;
export function increment() {
count++;
}
// main.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1(引用,值改变了)
// CommonJS - 值的拷贝
// counter.js
let count = 0;
function increment() {
count++;
}
module.exports = { count, increment };
// main.js
const { count, increment } = require('./counter.js');
console.log(count); // 0
increment();
console.log(count); // 0(拷贝,值不变)
10. 动态导入与代码分割
javascript
// 1. 动态import
button.addEventListener('click', async () => {
const module = await import('./heavy-module.js');
module.doSomething();
});
// 2. 条件导入
async function loadModule(type) {
if (type === 'admin') {
const admin = await import('./admin.js');
return admin.default;
} else {
const user = await import('./user.js');
return user.default;
}
}
// 3. React路由懒加载
import React, { lazy, Suspense } from 'react';
const AdminPanel = lazy(() => import('./AdminPanel'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<AdminPanel />
</Suspense>
);
}
// 4. Webpack代码分割
import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
console.log(_.chunk([1, 2, 3, 4], 2));
});