《编写可维护的 JavaScript》读书笔记

132 阅读5分钟

本文是对Nicholas C. Zakas所著《编写可维护的 JavaScript》一书的学习笔记,总结了书中关于如何编写可维护JavaScript代码的核心理念和最佳实践。

编程风格

基础格式化

  1. 缩进层次

    • 统一使用4个空格或2个空格作为一个缩进层级
    • 避免使用Tab,因为不同编辑器对Tab的解释可能不同
    • 在团队和项目中保持统一的缩进风格 (4个或2个可在团队内部统一规范)
  2. 语句结尾

    • 始终使用分号结尾 (有些团队也会约定都不写分号)
    // 推荐
    var name = 'John';
    var age = 25;
    
    // 不推荐
    var name = 'John'
    var age = 25
    
  3. 行的长度

    • 单行代码不超过80个字符
    • 超过限制时在运算符后换行
    • 换行后增加一个缩进层级
    // 推荐
    var result = something + anotherThing +
        yetAnotherThing + somethingElse;
    
  4. 空行

    • 在方法之间添加空行
    • 在逻辑相关的代码块之间添加空行
    • 在return语句前添加空行

合理添加空行,可以提高代码的可读性。

function doSomething() {
    var result = '';

    for (var i = 0; i < items.length; i++) {
        result += items[i];
    }

    return result;
}

function doSomethingElse() {
    // ...
}

命名规范

  1. 变量和函数命名

    • 变量名使用名词
    • 函数名使用动词开头
    • 使用驼峰命名法
    // 变量命名
    var firstName = 'John';
    var maxCount = 10;
    var isVisible = true;
    
    // 函数命名
    function getName() { }
    function setName(name) { }
    function findUserById(id) { }
    function handleBtnClick() { }
    
  2. 常量命名

    • 使用大写字母
    • 单词间用下划线分隔
    var MAX_COUNT = 10;
    var URL_PREFIX = 'http://example.com/';
    var COLOR_CODES = {
        RED: '#FF0000',
        GREEN: '#00FF00',
        BLUE: '#0000FF'
    };
    
  3. 构造函数命名

    • 使用大驼峰命名法(帕斯卡命名法)
    • 名称应该是名词
    function Person(name) {
        this.name = name;
    }
    
    function RequestFactory() {
        // ...
    }
    
  4. css 类命名 css 样式类名、关键帧、动画名同意使用短横线命名法。切勿使用大驼峰小驼峰等。

<div class="footer-btns">
<div>

注释

  1. 单行注释

    • 独占一行
    • 注释前空一行
    • 注释缩进与下一行代码保持一致
    function doSomething() {
        var result = '';
        
        // 检查输入值是否合法
        if (!isValid(result)) {
            return null;
        }
    }
    
  2. 多行注释

    • 使用JSDoc风格
    • 描述函数的功能、参数和返回值
    /**
     * 计算两个数字的和
     * @param {number} a 第一个数字
     * @param {number} b 第二个数字
     * @returns {number} 两个数字的和
     */
    function add(a, b) {
        return a + b;
    }
    

编程实践

变量声明

  1. 变量初始化

    • 所有变量在使用前都要声明
    • 优先使用const和let,避免使用var
    • 变量声明时尽可能初始化
    // 推荐
    const maxItems = 30;
    let currentCount = 0;
    let names = [];
    
    // 不推荐
    var items;
    var count;
    var result;
    
  2. null的使用场景

    • 初始化将来可能赋值为对象的变量
    • 作为对象原型链的终点
    • 作为函数的可选参数默认值
    let person = null;  // 将来会被赋值为一个对象
    function getPerson() {
        if (condition) {
            return new Person();
        }
        return null;    // 明确表示没有对象返回
    }
    

对象创建

  1. 使用字面量

    • 使用对象字面量创建对象
    • 使用数组字面量创建数组
    // 推荐
    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');
    
  2. 构造函数模式

    • 仅在需要创建具有相同属性和方法的多个对象时使用
    • 构造函数首字母大写
    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层的松耦合

  1. 将JavaScript从CSS中分离

    • 避免在JavaScript中操作样式
    • 使用修改类名的方式改变样式
    // 推荐
    element.classList.add('active');
    element.classList.remove('hidden');
    
    // 不推荐
    element.style.display = 'block';
    element.style.backgroundColor = 'red';
    
  2. 将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>
    
    

解耦模式

  1. 事件处理

    • 使用自定义事件解耦组件
    • 实现发布/订阅模式
    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);
                });
            }
        }
    };
    
  2. 数据层分离

    • 将配置数据从应用逻辑中分离
    • 使用配置对象存储可变数据
    const CONFIG = {
        apiUrl: 'https://api.example.com',
        pageSize: 20,
        features: {
            darkMode: true,
            notifications: false
        },
        messages: {
            welcome: '欢迎使用本系统',
            error: '发生错误,请稍后重试'
        }
    };
    

错误处理

  1. 异常捕获

    • 只捕获可能发生的特定错误
    • 提供有意义的错误信息
    try {
        JSON.parse(jsonString);
    } catch (e) {
        if (e instanceof SyntaxError) {
            console.error('JSON解析错误:', e.message);
        } else {
            throw e;  // 重新抛出未知错误
        }
    }
    
  2. 错误类型

    • 创建自定义错误类型
    • 继承内置Error类
    class ValidationError extends Error {
        constructor(message) {
            super(message);
            this.name = 'ValidationError';
        }
    }
    
    function validateUser(user) {
        if (!user.name) {
            throw new ValidationError('用户名不能为空');
        }
    }
    

编程技巧

  1. 避免全局变量

    • 使用模块模式
    • 使用命名空间
    const MyApp = {
        config: {
            // 配置信息
        },
        util: {
            // 工具函数
        },
        model: {
            // 数据模型
        }
    };
    
  2. 性能优化

    • 减少DOM操作
    • 使用事件委托
    • 避免频繁创建对象
    // 使用事件委托
    document.getElementById('list').addEventListener('click', function(e) {
        if (e.target.tagName === 'LI') {
            // 处理列表项点击
        }
    });
    

通过遵循以上规范和最佳实践,我们可以编写出更加可维护、可扩展的JavaScript代码。这些原则不仅能提高代码质量,也能帮助团队更好地协作和维护代码。记住,好的代码不仅要运行正确,更要易于理解和维护。