文章整理自dev.to感兴趣的可以去查看原文 https://dev.to/wizdomtek/best-practices-for-writing-clean-and-maintainable-code-in-javascript-17c 和dev.to/dawsoncodes…
干净和可维护的代码对于任何软件项目的长期成功和可扩展性都是至关重要的。它改善了团队成员之间的协作,减少了bug的可能性,并使代码更易于理解、测试和维护。在这篇文章中,我们将探索一些用JavaScript编写干净且可维护的代码的最佳实践,通过提供代码示例来说明每种实践。
一致的代码格式
一致的代码格式对于可读性至关重要。它可以帮助开发人员更快地理解代码并改善协作。使用一致且广泛接受的代码样式指南(如ESLint提供的指南),并配置编辑器或IDE以相应地自动设置代码格式。示例:
// Bad practice
function calculateSum(a,b){
return a + b;
}
// Good practice
function calculateSum(a, b) {
return a + b;
}
有意义的变量和函数名称
为变量、函数和类使用描述性的、有意义的名称。避免使用单字母变量名或缩写,以免混淆他人。这种做法增强了代码的可读性,并减少了对注释的需求。
在JavaScript中,标准是对变量和函数使用camel-case(如myVariableName
),对类使用Pascal case(如MyClassName
)。
示例:
// Bad practice
const x = 5;
// Good practice
const numberOfStudents = 5;
模块化和单一职责原则
遵循函数和类的单一职责原则。每个函数或类都应该有一个明确定义的单一职责。这种方法提高了代码的可重用性,并使测试、调试和维护更容易。
示例:
// Bad practice
function calculateSumAndAverage(numbers) {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
const average = sum / numbers.length;
return [sum, average];
}
// Good practice
function calculateSum(numbers) {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
function calculateAverage(numbers) {
const sum = calculateSum(numbers);
const average = sum / numbers.length;
return average;
}
避免使用全局变量
尽量减少全局变量的使用,因为它们会导致命名冲突,并使代码更难推理。相反,将代码封装在函数或模块中,并尽可能使用局部变量。
示例:
// Bad practice
let count = 0;
function incrementCount() {
count++;
}
// Good practice
function createCounter() {
let count = 0;
function incrementCount() {
count++;
}
return {
incrementCount,
getCount() {
return count;
}
};
}
const counter = createCounter();
counter.incrementCount();
错误处理和代码稳健性
优雅地处理错误并提供有意义的错误消息或适当地记录它们。验证输入,处理边缘情况,并使用适当的异常处理技术,如try-catch块。
示例:
// Bad practice
function divide(a, b) {
return a / b;
}
// Good practice
function divide(a, b) {
if (b === 0) {
throw new Error('不能被0除');
}
return a / b;
}
try {
const result = divide(10, 0);
console.log(result);
} catch (error) {
console.error(error.message);
}
避免代码重复
重复的代码不仅会导致代码臃肿,而且会使维护和错误修复更具挑战性。将可重用代码封装到函数或类中,并努力采用DRY(Don’t Repeat Yourself不要重复自己)方法。如果您发现自己在复制和粘贴代码,请考虑将其重构为可重用的函数或模块。
示例:
// Bad practice
function calculateAreaOfRectangle(length, width) {
return length * width;
}
function calculatePerimeterOfRectangle(length, width) {
return 2 * (length + width);
}
// Good practice
function calculateArea(length, width) {
return length * width;
}
function calculatePerimeter(length, width) {
return 2 * (length + width);
}
准确地使用注释
虽然干净的代码应该是自解释的,但在某些情况下,需要注释来提供额外的上下文或澄清复杂的逻辑。有节制地使用注释,使它们简洁而有意义。专注于解释“WHY”而不是“HOW“
示例
// Bad practice
function calculateTotalPrice(products) {
// 遍历产品
let totalPrice = 0;
for (let i = 0; i < products.length; i++) {
totalPrice += products[i].price;
}
return totalPrice;
}
// Good practice
function calculateTotalPrice(products) {
let totalPrice = 0;
for (let i = 0; i < products.length; i++) {
totalPrice += products[i].price;
}
return totalPrice;
// 总价格是通过对数组中所有产品的价格求和来计算的
}
优化性能
高效的代码可以提高应用程序的整体性能。注意不必要的计算、过多的内存使用和潜在的瓶颈。使用适当的数据结构和算法来优化性能。使用Chrome DevTools等工具分析和测量您的代码,以识别性能问题并相应地解决它们。
示例:有序数组查找
// 有序数组查找
// Bad practice
function findItemIndex(array, target) {
for (let i = 0; i < array.length; i++) {
if (array[i] === target) {
return i;
}
}
return -1;
}
// Good practice 使用二分法
function findItemIndex(array, target) {
let left = 0;
let right = array.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (array[mid] === target) {
return mid;
}
if (array[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
编写单元测试
单元测试对于确保代码的正确性和可维护性至关重要。编写自动化测试以覆盖不同的场景和边缘情况。这有助于及早捕获错误,促进代码重构,在修改代码时也更有信心。使用Jest或Mocha等测试框架来编写和运行测试。
示例(使用Jest):
// Code
function sum(a, b) {
return a + b;
}
// Test
test('sum function adds two numbers correctly', () => {
expect(sum(2, 3)).toBe(5);
expect(sum(-1, 5)).toBe(4);
expect(sum(0, 0)).toBe(0);
});
使用函数式编程概念
函数式编程概念,如不变性和纯函数,可以使代码更可预测,更容易推理。使用不可变的数据结构,尽可能避免改变对象或数组。编写没有副作用的纯函数,并为相同的输入生成相同的输出,使其更易于测试和调试。
示例:
// Bad practice
let total = 0;
function addToTotal(value) {
total += value;
}
// Good practice
function addToTotal(total, value) {
return total + value;
}
谨慎修改函数参数
直接修改对象的属性或作为函数参数传递的数组的值可能会导致不良的副作用和难以跟踪的错误。相反,请考虑返回一个新对象或数组。这种实践与函数式编程的原则非常一致,其中不变性是关键。
// ❌ 修改参数
function updateName (user) {
user.name = 'bob'
}
let user = { name: 'alice' }
updateName(user)
console.log(user) // { name: 'bob' }
// ✅ 避免改变函数参数,而是返回新对象
function updateName (user) {
return { ...user, name: 'bob' }
}
let user = { name: 'alice' }
let updatedUser = updateName(user)
console.log(user) // { name: 'alice' }
console.log(updatedUser) // { name: 'bob' }
使用JSDoc记录代码
使用JSDoc为函数、类和模块编写文档。这有助于其他开发人员理解您的代码,并使其更易于维护。
/**
* 两数求和.
* @param {number} a - 第一个数.
* @param {number} b - 第二个数.
* @returns {number} 两数之和.
*/
function add(a, b) {
return a + b;
}
使用 ESLint 和 Prettier等工具
// .eslintrc.json
{
"extends": ["eslint:recommended", "prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}
遵循 SoC(关注点分离)原则
为了保持简单和有组织,最好不要使用JavaScript来添加直接样式。这就是所谓的关注点分离(SoC)。相反,使用classList
API添加和删除类,并在CSS中定义样式。通过这种方式,CSS完成所有样式,JavaScript处理应用程序的所有其他功能。
这种编程概念并不局限于JavaScript,(SoC)关注点分离是一种分离功能
而不是混淆不同技术的实践。
// ❌ 避免使用 JavaScript 进行样式处理
let element = document.getElementById('my-element')
element.style.color = 'red'
// ✅ 通过 添加/删除 类更改样式
let element = document.getElementById('my-element')
element.classList.add('my-class')
使用 for...of
而不是 for
循环
ECMAScript 6中引入的for...of
语句是传统for
循环的更有效的替代方案。它带有一个内置的迭代器,消除了定义变量和长度值的需要。这会使你的代码更干净。
注意
:for of 不能用来遍历对象,因为它没有部署 Symbol.iterator 属性。
JavaScript中的类不会被提升
与函数不同,JavaScript中的类不会被提升,这意味着你需要在调用一个类之前声明它。这可能看起来违反直觉,特别是如果你习惯了函数提升,但这是一个基本原则,在JavaScript中使用类时必须理解和尊重。
// ❌ 在声明之前调用类:
const hat = new Hat('Red', 1000)
hat.show()
class Hat {
constructor (color, price) {
this.color = color
this.price = price
}
show () {
console.log(`This ${this.color} hat costs $${this.price}`)
}
}
// ✅ 在声明之后调用:
class Hat {
constructor (color, price) {
this.color = color
this.price = price
}
show () {
console.log(`This ${this.color} hat costs $${this.price}`)
}
}
const hat = new Hat('Red', 1000)
Promises 异常处理
在JavaScript的异步场景中,promise是一个流行的概念。但是,必须正确处理它们以防止意外行为。JavaScript提供了一个try...catch
块,可用于处理在执行异步代码期间可能出现的异常。通过这种方式,您可以确保错误被捕获并得到适当的处理,从而保持代码的健壮性。
// ❌ 未处理promise异常
async function fetchData () {
const response = await fetch('xxx')
const data = await response.json()
return data
}
// ✅ 处理promise异常
async function fetchData () {
try {
const response = await fetch('xxx')
const data = await response.json()
return data
} catch (error) {
// 在这里处理异常
throw new Error(error)
}
}
避免代码中过度嵌套
可以使用卫语句
来处理这种问题
// ❌ 嵌套代码块太多,不使用return关键字
function checkNumber (num) {
if (num > 0) {
console.log('Number is positive.')
} else {
if (num < 0) {
console.log('Number is negative.')
} else {
console.log('Number is zero.')
}
}
}
// ✅ 使用return关键字而不是else语句
function checkNumber (num) {
if (num > 0) {
console.log('Number is positive.')
return
}
if (num < 0) {
console.log('Number is negative.')
return
}
console.log('Number is zero.')
}
总结:
编写干净和可维护的代码不仅仅是个人喜好的问题;这是职业责任。通过遵循本文中概述的最佳实践,您可以提高JavaScript代码的质量,使其更易于理解,维护和协作,并确保软件项目的长期成功。一致性、可读性、模块化和错误处理是在努力获得干净和可维护的代码时要牢记的关键原则。快乐编码!