基于《编写可维护的JavaScript》的小组-编程实践-讨论稿

116 阅读4分钟

文章由于要拿来讨论制定小组开发规范和用于特定的场景,会比较“详尽",没有相应团队需求的掘友,节省时间,可以到别处掘金了。

编程实践是代码风格之外的另一种编程规范。在《编写可维护的JavaScript》第一版2013年发布后,相关技术又经过了飞速发展,但是书中很多建议仍然是比较好的实践方式。在这一部分中有当前我们小组开发过程中还在使用的或者遇到的问题,总结供小组讨论使用。

UI层的松耦合

当我们对一个组件一部分作出修改时,不会经常性的影响到其他部分,这种就是松耦合的一个具体表现。这会给我们的工作减轻很多繁琐的工作,提高小组的开发效率。

虽然现在很多前端框架都有将CSS、JavaScript、HTML这三个层的耦合变紧的情况,但始终认为,追求松耦合是一种更好的设计模式思想。如果使用了可以实现松耦合的前端技术栈,我们要尽量实现这三个层的松耦合和各组件模块间的松耦合。

避免使用全局变量

全局变量带来的问题

  • 命名冲突
  • 代码脆弱
  • 难以测试

事件处理

应用逻辑程序不应混杂在事件处理程序中,应当有明确的分工。事件对象event中包含很多和事件相关的额外信息,使用时不应随意分发事件对象。

const MyApplication = {

    // 事件
    handleClick: (event) => {

        // 阻止事件冒泡
        event.preventDefault();
        event.stopPropagation();

        // 传入应用逻辑
        this.showPopup(event.clientX, event,clientY);
    },

    // 应用逻辑
    showPopup: (x, y) => {
        let popup = document.getElementById("popup");
        popup.style.left = x + "px";
        popup.style.top = y + "px";
        popup.className = "reveal";
    }
};

避免-空比较

let intents = 0;

if (intents !== null) {
    intents.sort(); // TypeError: intents.sort is not a function

    intents.forEach(intent => {
        // 业务逻辑
    });
}

以上代码的if判断,并不能避免程序报错。

原始值判断

使用typeof检测除null之外的原始值是比较安全的做法。

let intentName = "收银设备不能启动怎么办",
    intentCount = 10,
    hasIntent = true,
    intentId,
    intentContent = undefined;

console.log(typeof intentName); // string
console.log(typeof intentCount); // number
console.log(typeof hasIntent); // boolean
console.log(typeof intentId); // undefined
console.log(typeof intentContent); // undefined
console.log(typeof intentStatus); // undefined

运行typeof null返回“object”,如果需要检测null,直接使用恒等运算符(===)或非恒等运算符(!==)。

引用值判断

console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof new Date()); // object
console.log(typeof new RegExp()); // object

不同引用值的typeof判断,都会返回object,所以不能像判断原始值一样用typeof来判断引用值。

console.log({} instanceof Array); // false
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true
console.log(new RegExp() instanceof RegExp); // true

console.log([] instanceof Object && new Date() instanceof Object && new RegExp() instanceof Object); // true

instanceof 可以用来检测某个引值类型,但是instanceof不仅检测构造这恶搞对象的构造器,还会检测原型链,因此每个对象的 instanceof Object 都会返回true,instanceof也可以检测自定义类型。

属性判断

let intent = {
    intentName: "打印机不能启动怎么处理",
    intentId: 0
};

if (intent['intentId']) {
    // 业务代码不会执行
}

if (intent['intentId'] != null) {
    // 业务代码不会执行
}

if (intent['intentId'] != undefined) {
    // 业务代码不会执行
}

以上3个if判断,实际上是通过给定的名字来判断属性的值,都不能正确判断intent对象中是否有intentId属性,当属性值为假值时,会返回错误。

let intent = {
    intentName: "打印机不能启动怎么处理",
    intentId: 0
};

if ('intentId' in intent) {
    // 业务代码会执行
}

if (intent.hasOwnProperty("intentId")) {
    // 业务代码会执行
}

in运算符会判断实例对象或者继承自对象的原型是否有相关属性,如果有返回true,hasOwnProperty只会检测实例对象是否有相关属性。

将配置数据从代码中分离出来

配置数据是应用中写死的值。

  • URL。
  • 需要展示给用户的字符串。
  • 重复使用的值。
  • 设置或初始化配置。
  • 任何可能发生变更的值。

可以将这些值,从代码中抽离,放在一个配置数据对象中,或者放在一个单独的配置文件中。

不要修改你创建之外的对象

如果你没有创建这些对象,不要修它们,它们是你项目执行环境的一部分,由于它们已经存在了,你可以直接使用这些或者用其来构建新的功能,而不能修改它们,这些对象包括:

  • 原生对象。
  • DOM对象。
  • 浏览器对象模型(BOM)对象 如:window
  • 类库对象。
// 覆盖DOM方法
document.getElementById = function () {
    return null;
}

// 新增DOM方法
document.getIntentName = function () {
    alert("intentName");
}

// 删除DOM方法
document.getElementById = null;

以上操作DOM方法的代码都将带来不可预知的问题。团队开发中其他伙伴用到相关的方法,都将产生错误,新增getIntentNameDOM方法,有可能在下一代标准中有同样命名的方法,也会导致使用相关方法产生问题。