错误处理机制
错误处理机制
JavaScript中的错误处理机制是保证代码健壮性的关键部分。当代码执行过程中出现异常时,如果没有适当的处理机制,程序可能会崩溃或产生不可预期的行为。通过合理的错误处理,可以捕获并处理这些异常,确保程序能够优雅地继续运行或提供有意义的反馈。
try-catch语句
try-catch
是最基本的错误处理结构。try
块包含可能抛出错误的代码,catch
块则捕获并处理这些错误。
try {
// 可能抛出错误的代码
const result = someFunctionThatMightFail();
console.log(result);
} catch (error) {
// 处理错误
console.error('An error occurred:', error.message);
}
catch
块接收一个错误对象,通常包含message
属性描述错误详情。还可以访问name
和stack
属性获取错误类型和调用栈信息。
finally块
finally
块无论是否发生错误都会执行,常用于清理资源。
let fileHandle;
try {
fileHandle = openFile('example.txt');
processFile(fileHandle);
} catch (error) {
console.error('File processing failed:', error);
} finally {
if (fileHandle) {
closeFile(fileHandle);
}
}
错误类型
JavaScript内置多种错误类型,可通过instanceof
检查:
try {
// 可能抛出不同类型的错误
} catch (error) {
if (error instanceof TypeError) {
console.error('Type error:', error.message);
} else if (error instanceof ReferenceError) {
console.error('Reference error:', error.message);
} else {
console.error('Unknown error:', error.message);
}
}
常见内置错误类型包括:
Error
:通用错误基类SyntaxError
:语法错误TypeError
:类型错误ReferenceError
:引用错误RangeError
:数值超出范围URIError
:URI处理错误EvalError
:eval函数错误
自定义错误
通过继承Error
类可以创建自定义错误类型:
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
function validateUser(user) {
if (!user.name) {
throw new ValidationError('Name is required', 'name');
}
if (!user.email) {
throw new ValidationError('Email is required', 'email');
}
}
try {
validateUser({});
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation failed for ${error.field}: ${error.message}`);
} else {
console.error('Unknown error:', error);
}
}
throw语句
使用throw
可以主动抛出错误,可以抛出任何值但最佳实践是抛出Error
对象或其子类实例。
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
try {
const result = divide(10, 0);
console.log(result);
} catch (error) {
console.error('Division error:', error.message);
}
Promise错误处理
Promise使用.catch()
方法处理错误,或通过try-catch
配合async/await
。
// 使用.catch()
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Fetch failed:', error));
// 使用async/await
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch failed:', error);
}
}
全局错误处理
window.onerror
捕获未被处理的运行时错误:
window.onerror = function(message, source, lineno, colno, error) {
console.error(`Uncaught error: ${message} at ${source}:${lineno}:${colno}`);
// 返回true阻止默认错误处理
return true;
};
unhandledrejection事件
捕获未被处理的Promise拒绝:
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled rejection:', event.reason);
// 阻止默认处理
event.preventDefault();
});
错误边界(React)
在React中,错误边界是捕获子组件树中JavaScript错误的组件:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// 使用
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
错误日志记录
生产环境中应将错误记录到服务器:
function logError(error) {
const errorData = {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href
};
fetch('/api/log-error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorData)
}).catch(loggingError => {
console.error('Failed to log error:', loggingError);
});
}
window.addEventListener('error', event => {
logError(event.error);
});
window.addEventListener('unhandledrejection', event => {
logError(event.reason);
});
防御性编程
结合错误处理的防御性编程实践:
// 1. 参数验证
function createUser(userData) {
if (!userData || typeof userData !== 'object') {
throw new TypeError('User data must be an object');
}
// ...
}
// 2. 默认值和可选链
function getAddress(user) {
return user?.address?.street ?? 'Unknown';
}
// 3. 空值合并
const config = userConfig ?? defaultConfig;
// 4. 类型检查
function calculateTotal(items) {
if (!Array.isArray(items)) {
throw new TypeError('Items must be an array');
}
// ...
}
异步迭代器的错误处理
处理异步迭代器时也需要特别注意错误:
async function processAsyncIterable(asyncIterable) {
try {
for await (const item of asyncIterable) {
console.log(item);
}
} catch (error) {
console.error('Async iteration failed:', error);
}
}
// 示例使用
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
async next() {
if (i++ < 3) {
await new Promise(resolve => setTimeout(resolve, 100));
return { value: i, done: false };
}
throw new Error('Async iteration error');
}
};
}
};
processAsyncIterable(asyncIterable);
Node.js中的错误处理
Node.js环境有一些特定的错误处理模式:
// 1. 回调函数的错误优先约定
fs.readFile('file.txt', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log(data.toString());
});
// 2. EventEmitter的错误处理
const eventEmitter = new EventEmitter();
eventEmitter.on('error', err => {
console.error('Emitter error:', err);
});
// 3. 使用domain模块(已废弃,但了解其概念很重要)
性能考虑
错误处理对性能的影响:
try-catch
块在错误未抛出时性能开销很小- 频繁抛出和捕获错误会影响性能
- 避免在热点代码路径中使用复杂的错误处理
- 生产环境应该最小化错误日志的数据量
// 不好的做法:在循环内部使用try-catch
for (let i = 0; i < 1000000; i++) {
try {
riskyOperation();
} catch (e) {
// ...
}
}
// 更好的做法:将整个循环包裹在try-catch中
try {
for (let i = 0; i < 1000000; i++) {
riskyOperation();
}
} catch (e) {
// ...
}
测试错误场景
编写测试验证错误处理逻辑:
// 使用Jest测试错误抛出
test('divide throws error when dividing by zero', () => {
expect(() => divide(10, 0)).toThrow('Division by zero');
expect(() => divide(10, 0)).toThrow(Error);
});
// 测试异步错误
test('async function rejects properly', async () => {
await expect(fetchData('invalid-url')).rejects.toThrow('Network error');
});
// 测试React错误边界
test('ErrorBoundary catches child errors', () => {
const ErrorComponent = () => {
throw new Error('Test error');
};
const wrapper = mount(
<ErrorBoundary>
<ErrorComponent />
</ErrorBoundary>
);
expect(wrapper.text()).toContain('Something went wrong');
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn