本文是对Nicholas C. Zakas所著《编写可维护的 JavaScript》一书的学习笔记,总结了书中关于如何编写可维护JavaScript代码的核心理念和最佳实践。
编程风格
基础格式化
-
缩进层次
- 统一使用4个空格或2个空格作为一个缩进层级
- 避免使用Tab,因为不同编辑器对Tab的解释可能不同
- 在团队和项目中保持统一的缩进风格 (4个或2个可在团队内部统一规范)
-
语句结尾
- 始终使用分号结尾 (有些团队也会约定都不写分号)
// 推荐 var name = 'John'; var age = 25; // 不推荐 var name = 'John' var age = 25 -
行的长度
- 单行代码不超过80个字符
- 超过限制时在运算符后换行
- 换行后增加一个缩进层级
// 推荐 var result = something + anotherThing + yetAnotherThing + somethingElse; -
空行
- 在方法之间添加空行
- 在逻辑相关的代码块之间添加空行
- 在return语句前添加空行
合理添加空行,可以提高代码的可读性。
function doSomething() {
var result = '';
for (var i = 0; i < items.length; i++) {
result += items[i];
}
return result;
}
function doSomethingElse() {
// ...
}
命名规范
-
变量和函数命名
- 变量名使用名词
- 函数名使用动词开头
- 使用驼峰命名法
// 变量命名 var firstName = 'John'; var maxCount = 10; var isVisible = true; // 函数命名 function getName() { } function setName(name) { } function findUserById(id) { } function handleBtnClick() { } -
常量命名
- 使用大写字母
- 单词间用下划线分隔
var MAX_COUNT = 10; var URL_PREFIX = 'http://example.com/'; var COLOR_CODES = { RED: '#FF0000', GREEN: '#00FF00', BLUE: '#0000FF' }; -
构造函数命名
- 使用大驼峰命名法(帕斯卡命名法)
- 名称应该是名词
function Person(name) { this.name = name; } function RequestFactory() { // ... } -
css 类命名 css 样式类名、关键帧、动画名同意使用短横线命名法。切勿使用大驼峰小驼峰等。
<div class="footer-btns">
<div>
注释
-
单行注释
- 独占一行
- 注释前空一行
- 注释缩进与下一行代码保持一致
function doSomething() { var result = ''; // 检查输入值是否合法 if (!isValid(result)) { return null; } } -
多行注释
- 使用JSDoc风格
- 描述函数的功能、参数和返回值
/** * 计算两个数字的和 * @param {number} a 第一个数字 * @param {number} b 第二个数字 * @returns {number} 两个数字的和 */ function add(a, b) { return a + b; }
编程实践
变量声明
-
变量初始化
- 所有变量在使用前都要声明
- 优先使用const和let,避免使用var
- 变量声明时尽可能初始化
// 推荐 const maxItems = 30; let currentCount = 0; let names = []; // 不推荐 var items; var count; var result; -
null的使用场景
- 初始化将来可能赋值为对象的变量
- 作为对象原型链的终点
- 作为函数的可选参数默认值
let person = null; // 将来会被赋值为一个对象 function getPerson() { if (condition) { return new Person(); } return null; // 明确表示没有对象返回 }
对象创建
-
使用字面量
- 使用对象字面量创建对象
- 使用数组字面量创建数组
// 推荐 const person = { name: 'John', age: 30 }; const colors = ['red', 'green', 'blue']; // 不推荐 const person = new Object(); person.name = 'John'; person.age = 30; const colors = new Array('red', 'green', 'blue'); -
构造函数模式
- 仅在需要创建具有相同属性和方法的多个对象时使用
- 构造函数首字母大写
function Person(name, age) { this.name = name; this.age = age; this.sayName = function() { return this.name; }; } const person1 = new Person('John', 30); const person2 = new Person('Jane', 25);
松耦合设计
UI层的松耦合
-
将JavaScript从CSS中分离
- 避免在JavaScript中操作样式
- 使用修改类名的方式改变样式
// 推荐 element.classList.add('active'); element.classList.remove('hidden'); // 不推荐 element.style.display = 'block'; element.style.backgroundColor = 'red'; -
将JavaScript从HTML中分离
- 避免内联事件处理程序
- 使用事件委托
- 使用unobtrusive JavaScript模式
// 不推荐的方式 - HTML中包含大量JavaScript代码 <form onsubmit="if(validateName() && validateEmail()) { submitForm(); } else { return false; }"> <input type="text" onblur="validateName()" /> <input type="email" onblur="validateEmail()" /> <button onclick="submitForm()">提交</button> </form> // 推荐的方式 - HTML保持简洁 <!-- HTML --> <form id="userForm" class="registration-form"> <input type="text" name="username" class="form-input" /> <input type="email" name="email" class="form-input" /> <button type="submit">提交</button> </form>
解耦模式
-
事件处理
- 使用自定义事件解耦组件
- 实现发布/订阅模式
const EventEmitter = { events: {}, on(event, callback) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(callback); }, emit(event, data) { if (this.events[event]) { this.events[event].forEach(callback => { callback(data); }); } } }; -
数据层分离
- 将配置数据从应用逻辑中分离
- 使用配置对象存储可变数据
const CONFIG = { apiUrl: 'https://api.example.com', pageSize: 20, features: { darkMode: true, notifications: false }, messages: { welcome: '欢迎使用本系统', error: '发生错误,请稍后重试' } };
错误处理
-
异常捕获
- 只捕获可能发生的特定错误
- 提供有意义的错误信息
try { JSON.parse(jsonString); } catch (e) { if (e instanceof SyntaxError) { console.error('JSON解析错误:', e.message); } else { throw e; // 重新抛出未知错误 } } -
错误类型
- 创建自定义错误类型
- 继承内置Error类
class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; } } function validateUser(user) { if (!user.name) { throw new ValidationError('用户名不能为空'); } }
编程技巧
-
避免全局变量
- 使用模块模式
- 使用命名空间
const MyApp = { config: { // 配置信息 }, util: { // 工具函数 }, model: { // 数据模型 } }; -
性能优化
- 减少DOM操作
- 使用事件委托
- 避免频繁创建对象
// 使用事件委托 document.getElementById('list').addEventListener('click', function(e) { if (e.target.tagName === 'LI') { // 处理列表项点击 } });
通过遵循以上规范和最佳实践,我们可以编写出更加可维护、可扩展的JavaScript代码。这些原则不仅能提高代码质量,也能帮助团队更好地协作和维护代码。记住,好的代码不仅要运行正确,更要易于理解和维护。