前端常见规范、数组遍历方式

提高代码可读性

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)
forEachundefined简单遍历O(n)
map新数组元素转换O(n)
filter新数组元素筛选O(n)
reduce任意类型累计计算O(n)
some/everyboolean存在性检查O(n)~O(1)
find元素/undefined查找元素O(n)
for…of-可中断的遍历O(n)
entries()迭代器需要索引的遍历O(n)

五、最佳实践指南

  1. 优先选择声明式方法

    // 优于 for 循环
    const validItems = items.filter(isValid);
    
  2. 链式调用优化

    // 合并map和filter
    const result = arr
      .map(x => x * 2)
      .filter(x => x > 10);
    
  3. 性能敏感场景用基础循环

    // 大数据量时优先使用for
    for (let i = 0; i < 1000000; i++) { ... }
    
  4. 合理使用短路特性

    // 发现无效项立即退出
    if (arr.some(item => !isValid(item))) {
      throw new Error('包含无效数据');
    }
    
  5. 注意箭头函数特性

    // 需要this绑定使用普通函数
    arr.forEach(function(item) {
      this.handleItem(item); 
    }, this);
    
  6. 避免副作用

    // Bad: 修改外部变量
    let total = 0;
    arr.forEach(n => total += n);
    
    // Good: 使用reduce
    const total = arr.reduce((sum, n) => sum + n, 0);
    

JavaScript 中的可预知错误处理

在 JavaScript 开发中,我们经常会遇到错误,这些错误大致可以分为两类:

  1. 可预知错误(Predictable Errors):例如用户输入错误、API 请求失败、权限不足等。
  2. 不可预知错误(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.onerrorunhandledrejection 进行全局监听。

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"]
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值