提高代码可读性
1. 命名规范
// ❌ 糟糕的命名
let d = 10;
function proc(u) { ... }
// ✅ 清晰的命名
const MAX_RETRY_COUNT = 10; // 常量全大写
function calculateMonthlyRevenue(users) { ... } // 动词+名词结构
// ✅ 组件用驼峰的命名
import ListPage from "@/components/ListPage/index.vue";
### 布尔变量规范
// ✅ 推荐格式
const isAdmin = true;
const hasPermission = checkAuth(user);
2. 函数设计
### 单一职责原则
// ❌ 混杂多个职责
function handleUserData(user) {
if (user.age >= 18) { // 验证
const data = format(user); // 格式化
saveToDB(data); // 持久化
}
}
// ✅ 拆分职责
function isAdult(user) {
return user.age >= 18;
}
function formatUserData(user) {
return { ...user, name: user.name.trim() };
}
async function handleUser(user) {
if (!isAdult(user)) return;
const cleanData = formatUserData(user);
await saveToDB(cleanData);
}
3. 异步处理
避免回调地狱
// ❌ 嵌套回调
getUser(id, (user) => {
getOrders(user.id, (orders) => {
processOrders(orders, (res) => {
updateUI(res);
});
});
});
// ✅ Async/Await
async function loadData(userId) {
const user = await getUser(userId);
const orders = await getOrders(user.id);
return await processOrders(orders);
}
// ✅ Promise 链
getUser(id)
.then(user => getOrders(user.id))
.then(orders => processOrders(orders))
.then(updateUI);
4. 条件判断
卫语句优化
// ❌ 多层嵌套
function checkAuth(user) {
if (user != null) {
if (user.isActive) {
return user.roles.includes('admin');
}
}
return false;
}
// ✅ 提前返回
function checkAuth(user) {
if (!user) return false;
if (!user.isActive) return false;
return user.roles.includes('admin');
}
5. 常量管理
消灭魔法值
// ❌ 直接使用原始值
if (status === 4) {
cancelOrder();
}
// ✅ 语义化常量
const ORDER_STATUS = {
CANCELLED: 4,
COMPLETED: 5
};
if (status === ORDER_STATUS.CANCELLED) {
handleCancellation();
}
6. 结构优化
利用解构赋值
// ❌ 冗长的属性访问
function printProfile(user) {
console.log(`${user.name}, ${user.address.city}`);
}
// ✅ 解构 + 默认值
function printProfile({ name, address: { city = 'Unknown' } }) {
console.log(`${name}, ${city}`);
}
7. 模块化
功能拆分
// ✅ 工具模块
// utils/logger.js
export function logError(message) {
console.error(`[${new Date().toISOString()}] ERROR: ${message}`);
}
// ✅ 业务模块
// services/userService.js
export async function fetchUser(id) {
try {
return await axios.get(`/users/${id}`);
} catch (err) {
logError(`User ${id} fetch failed`);
throw err;
}
}
8. 注释规范
// ❌ 描述代码行为
function sort(list) {
list.sort((a, b) => b - a); // 降序排序
}
// ✅ 说明业务背景
function sortProducts(products) {
// 按价格降序排列以匹配商城首页的推荐规则(PRD v2.3.5)
products.sort((a, b) => b.price - a.price);
}
// 单行注释和多行注释使用场景
1.单行解释说明用单行注释
function calculate() {
return value * 0.15; // TODO: 税率需要动态配置
}
2.复杂业务函数用多行注释
/**
* 计算商品折扣价
* @param {number} basePrice - 基准价格
* @param {number} discountRate - 折扣率 (0-1)
* @returns {number} 折扣后价格
*/
function calculateDiscount(basePrice, discountRate) {
return basePrice * (1 - discountRate);
}
9. 错误处理
统一异常处理
// ✅ 错误处理中间件
async function fetchData(url) {
try {
const res = await fetch(url);
return await res.json();
} catch (err) {
logError(err); // 记录错误日志
throw new AppError('FETCH_FAILED', '数据获取失败'); // 自定义错误
}
}
10. 工具辅助
自动化代码质量
// .eslintrc.js
{
"rules": {
"semi": ["error", "always"], // 强制分号
"indent": ["error", 2], // 2空格缩进
"quotes": ["error", "single"], // 单引号
"no-unused-vars": "warn" // 未使用变量警告
}
}
数组遍历的方式、应用场景、优缺点
一、基础遍历方法
1. for
循环
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
- 应用场景:需要索引操作、复杂控制流程
- 优点:
- 完全控制循环过程(可中断、可跳过)
- 最佳性能(无函数调用开销)
- 缺点:
- 代码冗余
- 需要手动处理索引
2. while
循环
let i = 0;
while (i < arr.length) {
console.log(arr[i]);
i++;
}
- 适用场景:不确定循环次数的遍历
- 优点:灵活处理复杂条件
- 缺点:容易造成无限循环
二、ES5+ 高阶函数
3. forEach
arr.forEach((item, index) => {
console.log(index, item);
});
- 应用场景:简单遍历不需要返回值
- 优点:
- 语法简洁
- 自动处理索引
- 缺点:
- 无法中断循环(不能使用break)
- 没有返回值
4. map
const doubled = arr.map(num => num * 2);
- 应用场景:转换数组元素生成新数组
- 优点:
- 函数式编程风格
- 自动返回新数组
- 缺点:
- 性能略低于for循环
- 必须返回新元素
5. filter
const evens = arr.filter(num => num % 2 === 0);
- 应用场景:筛选符合条件的元素
- 优点:
- 声明式筛选逻辑
- 自动返回新数组
- 缺点:
- 需要遍历全部元素
- 性能敏感场景慎用
6. reduce
const sum = arr.reduce((acc, cur) => acc + cur, 0);
- 应用场景:累计计算(求和、统计、扁平化)
- 优点:
- 强大的累积计算能力
- 可处理复杂数据结构
- 缺点:
- 学习曲线较高
- 代码可读性较差
7. some
/ every
const hasNegative = arr.some(num => num < 0);
const allPositive = arr.every(num => num > 0);
- 应用场景:存在性检查(至少一个/全部满足条件)
- 优点:
- 短路特性(找到结果即停止)
- 语义清晰
- 缺点:
- 只能返回布尔值
- 无法获取具体元素
8. find
/ findIndex
const target = arr.find(item => item.id === 123);
const index = arr.findIndex(item => item.value > 100);
- 应用场景:查找元素/索引
- 优点:
- 短路特性
- 直接返回目标
- 缺点:
- 只能返回第一个匹配项
- 未找到返回undefined/-1
三、ES6+ 新增方法
9. for...of
for (const item of arr) {
if (item === 2) break; // 可中断
console.log(item);
}
- 应用场景:需要中断的简单遍历
- 优点:
- 支持break/continue
- 无需处理索引
- 缺点:
- 无法直接获取索引
- 兼容性问题(需Babel转换)
10. entries()
遍历
for (const [index, item] of arr.entries()) {
console.log(index, item);
}
- 应用场景:需要同时获取索引和元素
- 优点:
- 解构赋值语法
- 支持中断
- 缺点:
- 语法稍复杂
- 性能略低
11. flatMap
(ES2019)
const phrases = ["Hello world", "Good morning"];
const words = phrases.flatMap(phrase => phrase.split(' '));
// ["Hello", "world", "Good", "morning"]
- 应用场景:映射后扁平化数组
- 优点:
- 合并map和flat操作
- 自动过滤空项
- 缺点:
- 浏览器兼容性要求高
- 深度只支持一级
四、方法对比表
方法 | 返回值 | 改变原数组 | 可中断 | 最佳场景 | 时间复杂度 |
---|---|---|---|---|---|
for | - | ❌ | ✅ | 需要精细控制流程 | O(n) |
forEach | undefined | ❌ | ❌ | 简单遍历 | O(n) |
map | 新数组 | ❌ | ❌ | 元素转换 | O(n) |
filter | 新数组 | ❌ | ❌ | 元素筛选 | O(n) |
reduce | 任意类型 | ❌ | ❌ | 累计计算 | O(n) |
some/every | boolean | ❌ | ✅ | 存在性检查 | O(n)~O(1) |
find | 元素/undefined | ❌ | ✅ | 查找元素 | O(n) |
for…of | - | ❌ | ✅ | 可中断的遍历 | O(n) |
entries() | 迭代器 | ❌ | ✅ | 需要索引的遍历 | O(n) |
五、最佳实践指南
-
优先选择声明式方法
// 优于 for 循环 const validItems = items.filter(isValid);
-
链式调用优化
// 合并map和filter const result = arr .map(x => x * 2) .filter(x => x > 10);
-
性能敏感场景用基础循环
// 大数据量时优先使用for for (let i = 0; i < 1000000; i++) { ... }
-
合理使用短路特性
// 发现无效项立即退出 if (arr.some(item => !isValid(item))) { throw new Error('包含无效数据'); }
-
注意箭头函数特性
// 需要this绑定使用普通函数 arr.forEach(function(item) { this.handleItem(item); }, this);
-
避免副作用
// Bad: 修改外部变量 let total = 0; arr.forEach(n => total += n); // Good: 使用reduce const total = arr.reduce((sum, n) => sum + n, 0);
JavaScript 中的可预知错误处理
在 JavaScript 开发中,我们经常会遇到错误,这些错误大致可以分为两类:
- 可预知错误(Predictable Errors):例如用户输入错误、API 请求失败、权限不足等。
- 不可预知错误(Unpredictable Errors):例如服务器崩溃、未知的运行时异常等。
本篇文章主要讨论 可预知错误 的处理方式,以提升代码的健壮性。
1. 使用 try…catch 捕获同步错误
对于可以预见的异常,例如 JSON 解析失败,可以使用 try...catch
进行捕获。
function parseJSON(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error("JSON 解析失败:", error.message);
return null;
}
}
const data = parseJSON('{"name": "Alice"}'); // 正常解析
const invalidData = parseJSON('{name: Alice}'); // 解析失败
适用场景
- 解析 JSON 数据
- 处理本地存储(
localStorage
/sessionStorage
)读取失败 - 解析用户输入
- 处理同步计算错误
2. 处理异步错误(Promise & async/await)
2.1 使用 .catch()
处理 Promise 错误
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log("获取数据:", data))
.catch(error => console.error("API 请求失败:", error.message));
2.2 使用 try...catch
处理 async/await 错误
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP 错误!状态码: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("获取数据失败:", error.message);
return null;
}
}
fetchData("https://api.example.com/data").then(data => {
if (data) {
console.log("数据:", data);
}
});
适用场景
- API 请求
- 读取远程资源
- 处理异步任务错误
- 处理数据库查询异常
3. 使用全局错误捕获
对于 未捕获 的错误,可以使用 window.onerror
或 unhandledrejection
进行全局监听。
3.1 捕获运行时错误
window.onerror = function (message, source, lineno, colno, error) {
console.error("全局错误捕获:", message, "来源:", source, "行号:", lineno);
};
// 示例错误
console.log(undeclaredVariable);
3.2 捕获未处理的 Promise 错误
window.addEventListener("unhandledrejection", event => {
console.error("未处理的 Promise 错误:", event.reason);
});
// 示例错误
Promise.reject(new Error("Unhandled rejection example"));
适用场景
- 监控未处理的异常
- 记录错误日志
- 提示用户错误信息
- 预防因 Promise 未捕获导致的潜在问题
4. 设计良好的错误对象
自定义错误对象可以提供更好的错误信息,方便调试。
class CustomError extends Error {
constructor(message, errorCode) {
super(message);
this.name = "CustomError";
this.code = errorCode;
}
}
try {
throw new CustomError("无效的用户输入", 400);
} catch (error) {
console.error(`${error.name} (${error.code}): ${error.message}`);
}
适用场景
- 自定义业务逻辑错误
- 提供更详细的错误信息
- 统一错误格式
- 便于错误分类处理
5. 错误恢复策略
错误发生后,除了提示用户或记录日志,还可以尝试恢复系统运行。
5.1 提供默认值
function getUserName(user) {
return user?.name || "未知用户";
}
console.log(getUserName(null)); // "未知用户"
5.2 自动重试
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error("请求失败");
return await response.json();
} catch (error) {
console.warn(`请求失败,重试 ${i + 1}/${retries}`);
if (i === retries - 1) throw error;
}
}
}
5.3 记录错误日志
function logError(error) {
console.error("错误日志:", {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
});
}
try {
throw new Error("数据库连接失败");
} catch (error) {
logError(error);
}
6. 使用 ESLint 预防可预知错误
ESLint 可以在代码编写阶段就发现潜在错误,避免运行时崩溃。
6.1 常见 ESLint 规则
{
"rules": {
"no-undef": "error", // 禁止使用未定义的变量
"no-unused-vars": "warn", // 警告未使用的变量
"eqeqeq": "error", // 强制使用 === 而不是 ==
"no-console": "warn" // 避免滥用 console
}
}
6.2 结合 TypeScript 使用 ESLint
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
在 .eslintrc.json
配置 TypeScript 规则:
{
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"]
}
6.3 结合 Prettier 统一代码风格
npm install --save-dev prettier eslint-config-prettier
在 .eslintrc.json
中添加:
{
"extends": ["prettier"]
}