如何编写高质量 JavaScript 代码:易读、高性能与健壮性指南

作者:极速慢跑时间:2026-10-25 03:51:05浏览:211

本文深入探讨如何编写高质量的 JavaScript 代码,从易读性、高性能和健壮性三个维度提供实用建议。内容涵盖统一代码格式、消除魔术数字、单一职责、减少嵌套、算法优化、内置方法使用、作用域链管理、缓存机制、新语法应用、纯函数设计以及逻辑关注点整合等核心技巧,帮助开发者提升代码质量与可维护性。

近期,“人类高质量男性”相关视频在网络上引发热议,相信不少读者都曾接触过。借此话题,本文将探讨何为“人类高质量代码”,这虽是一句玩笑,却引出了我们对代码质量的思考。

本文旨在分享个人在日常开发中总结的一些实用技巧,期望能起到抛砖引玉的作用,为读者提供些许启发与帮助。

接下来,我们将一起探讨如何编写出高质量的 JavaScript 代码。

一、易阅读的代码

首先强调,代码的首要读者是开发者自己或团队成员,良好的可读性是编写高质量代码的基础。为此,我们总结了四点具体操作建议。

1、统一代码格式

避免代码风格忽左忽右,应尽量保持写法统一,以下通过示例说明。


// bad
function foo(x,y) {
  return {
    sum : x + y
  };
}
function bar(m, n){
  let ret = m*n
  return ret;
}
// good
function foo(x, y) {    //  适当的空格隔开,一般符号前不添加空格,符号后添加空格
  return {
    sum: x + y,         //  拖尾逗号是合法的,简化了对象和数组添加或删除元素
  }                     //  省略结束分号,当然需要知道如何规避风险
}
function bar(m, n) {
  let ret = m * n
  return ret
}


人工约定代码格式往往效率低下,建议借助自动化工具进行格式转换,例如使用 prettier 插件(https://prettier.io/)。

2、去除魔术数字

魔术数字(magic number)指的是在代码中直接书写的具体数值(如“10”、“123”等)。虽然代码作者撰写时可能清楚其含义,但对于其他开发者,甚至作者本人在一段时间后,都很难理解该数值的具体用途。


// bad
setTimeout(blastOff, 86400000)
document.onkeydown = function (ev) {
  if (ev.keyCode === 13) {
    // todos
  }
}
// good
const MILLISECONDS_IN_A_DAY = 86400000
const ENTER_KEY = 13
setTimeout(blastOff, MILLISECONDS_IN_A_DAY)
document.onkeydown = function (ev) {
  if (ev.keyCode === ENTER_KEY) {
    // todos
  }
}


处理魔术字符串的方法与此类似。在上述代码示例中,建议常量命名采用下划线风格,而变量和函数则推荐使用驼峰命名法。

同理,减少 this 的使用频率也有助于提升代码可读性。当代码中充斥大量 this 时,往往难以判断其指向,导致阅读成本大幅增加。


// bad
class Foo {
    foo() {
        this.number = 100
        this.el.onclick = function () {
            this.className = "active"
        }
    }
}
// good
class Foo {
    foo() {
        let context = this
        context.number = 100
        context.el.onclick = function () {
            let el = this
            el.className = "active"
        }
    }
}

3、单一功能原则

无论是编写模块、类还是函数,都应确保其仅具备单一功能,避免承担过多职责。这样不仅阅读起来简单明了,后续扩展也会更加灵活。


// bad
function copy(obj, deep) {
  if (deep) {
    // 深拷贝
  } else {
    // 浅拷贝
  }
}
// good
function copy(obj) {
  // 浅拷贝
}
function deepCopy(obj) {
  // 深拷贝
}

4、减少嵌套层级

多层级的嵌套结构,例如条件嵌套、循环嵌套或回调嵌套,会严重阻碍代码阅读,因此应尽量减少嵌套层级。

针对条件嵌套问题,通常可采用卫语句(guard clause)的方式提前返回,从而有效减少嵌套。


// bad
function foo() {
  let result
  if (isDead) {
    result = deadAmount()
  } else {
    if (isRet) {
      result = retAmount()
    } else {
      result = normalAmount()
    }
  }
  return result
}
// good
function foo() {
  if (isDead) {
    return deadAmount()
  }
  if (isRet) {
    return retAmount()
  }
  return normalAmount()
}

除了卫语句,还可以利用短路运算或条件运算符来改写条件语句,以简化逻辑。


// bad
function foo() {
    if (isOk) {
        todo()
    }
    let grade
    if (isAdmin) {
        grade = 1
    } else {
        grade = 0
    }
}
// good
function foo() {
    isOk && todo()                   // 短路运算
    let grade = isAdmin ? 1 : 0      // 条件运算符
}

针对回调嵌套问题,一般推荐使用“async/await”方式进行重构。


// bad
let fs = require("fs")
function init() {
  fs.mkdir(root, (err) => {
    fs.mkdir(path.join(root, "public", "stylesheets"), (err) => {
      fs.writeFile(
        path.join(root, "public", "stylesheets", "style.css"),
        "",
        function (err) {}
      )
    })
  })
}
init()
// good
let fs = require("fs").promises
async function init() {
  await fs.mkdir(root)
  await fs.mkdir(path.join(root, "public", "stylesheets"))
  await fs.writeFile(path.join(root, "public", "stylesheets", "style.css"), "")
}
init()

除上述四点建议外,还有许多改善阅读体验的方法,例如添加有效注释、避免不同类型比较以及避免使用晦涩语法等。

二、高性能的代码

在软件开发中,代码性能直接影响用户体验,因此高质量代码必然具备高性能特征。以下总结四点具体操作建议。

提示:测试 JavaScript 平均耗时,可使用 console.time() 方法、JSBench.Me 工具、performance 工具等。

1、优化算法

递归是一种常见算法,以下示例展示了使用递归实现“求阶乘”的操作。


// bad
function foo(n) {
  if (n === 1) {
    return 1
  }
  return n * foo(n - 1)
}
foo(100)   // 平均耗时:0.47ms
// good
function foo(n, result = 1) {
  if (n === 1) {
    return result
  }
  return foo(n - 1, n * result)    // 这里尾调用优化
}
foo(100)   // 平均耗时:0.09ms


“尾调用”是一种可复用栈帧的内存管理优化机制,即外部函数的返回值直接作为内部函数的返回值。

2、使用内置方法

许多功能可通过 JavaScript 内置方法解决。内置方法的底层实现通常是最优的,且可在解释器中提前执行,因此执行效率极高。

以下示例展示了如何获取对象属性和值的复合数组形式。


// bad
let data = {
  username: "leo",
  age: 20,
  gender: "male",
}
let result = []
for (let attr in data) {
  result.push([attr, data[attr]])
}
console.log(result)
// good
let data = {
  username: "leo",
  age: 20,
  gender: "male",
}
let result = Object.entries(data)
console.log(result)

3、减少作用域链查找

作用域链是作用域规则的实现机制,通过它,变量可在其作用域内被访问,函数可在其作用域内被调用。作用域链是一个单向链表,链表上的每个节点代表执行上下文的变量对象(代码执行时即活动对象)。单向链表的头部(首个可访问节点)始终是当前正在执行的函数的变量对象(活动对象),而尾部始终指向全局活动对象。

若概念过于复杂,可参考以下图示。

一、易阅读的代码

该作用域链表现为 3(头部:bar) -> 2(foo) -> 1(尾部:全局)。因此,在查找变量时,应尽可能在头部完成获取,以节省性能。具体对比如下。


// bad
function foo() {
  $("li").click(function () {     // 全局查找一次
    $("li").hide()                // 再次全局查找一次
    $(this).show()
  })
}
// good
function foo() {
  let $li = $("li")               // 减少下面$li的作用域查找层级      
  $li.click(function () {      
    $li.hide()               
    $(this).show()
  })
}


除了减少作用域链查找,减少对象属性查找也具有类似的优化效果。


// bad
function isNull(arg) {
  return Object.prototype.toString.call(arg) === "[object Null]"
}
function isFunction(arg) {
  return Object.prototype.toString.call(arg) === "[object Function]"
}
// good
let toString = Object.prototype.toString
function isNull(arg) {
  return toString.call(arg) === "[object Null]"
}
function isFunction(arg) {
  return toString.call(arg) === "[object Function]"
}

4、避免做重复的代码

编程时若出现大量重复执行的代码,应尽量避免重复操作。以下是一个简单示例,通过循环查找第一个满足条件元素的索引位置。


// bad
let index = 0
for (let i = 0, len = li.length; i < len; i++) {
    if (li[i].dataset.switch === "on") {
        index = i
    }
}
// good
let index = 0
for (let i = 0, len = li.length; i < len; i++) {
    if (li[i].dataset.switch === "on") {
        index = i
        break        // 后面的循环没有意义,属于执行不必要的代码
    }
}


再看一个计算“斐波那契数列”的案例。


// bad
function foo(n) {
  if (n < 3) {
    return 1
  }
  return foo(n - 1) + foo(n - 2)
}
foo(40)     // 平均耗时:1043ms
// good
let cache = {}
function foo(n) {
  if (n < 3) {
    return 1
  }
  if (!cache[n]) {
    cache[n] = foo(n - 1) + foo(n - 2)
  }
  return cache[n]
}
foo(40)    // 平均耗时:0.16ms


此处将递归执行过的结果缓存至数组中,后续重复代码可直接读取缓存数据,从而大幅提升性能。

一、易阅读的代码

图中画叉部分将直接走缓存,而不会重复执行计算过程。

除上述四点建议外,还有诸多提升代码性能的方法,例如减少 DOM 操作、实施节流处理以及使用事件委托等。

三、健壮性的代码

所谓健壮性代码,是指具备可扩展、可维护和可测试特性的代码。以下总结四点具体操作建议。

1、使用新语法

许多新语法可弥补旧语法的缺陷,使代码更加健壮,以应对未来变化。


// bad
var a = 1
isNaN(NaN)              // true
isNaN(undefined)        // true
// good
let a = 1
Number.isNaN(NaN)       // true
Number.isNaN(undefined) // false


新语法还能简化原有操作,使代码结构更加清晰。


// bad
let user = { name: "james", age: 36 }
function foo() {
  let arg = arguments
  let name = user.name
  let age = user.age
}
// good
let user = { name: "james", age: 36 }
function foo(...arg) {          // 剩余参数
  let { name, age } = user      // 解构赋值
}

2、随时可扩展

由于产品需求常发生变更,对软件的可扩展性提出了较高要求。因此,健壮的代码应具备随时调整的能力。


// bad
function foo(animal) {
  if (animal === "dog" || animal === "cat") {
    // todos
  }
}
function bar(name, age) {}
bar("james", 36)
// good
function foo(animal) {
  const animals = ["dog", "cat", "hamster", "turtle"]   // 可扩展匹配值
  if (animals.includes(animal)) {
    // todos
  }
}
function bar(options) {}    // 可扩展任意参数
bar({
  gender: "male",
  name: "james",
  age: 36,
})

3、避免副作用

当函数产生了除“接收一个值并返回一个结果”之外的行为时,即产生了副作用。副作用并非一定有害,但若在项目中无节制地引发副作用,代码出错的可能性将显著增加。

建议尽量避免修改全局变量或可变对象,通过参数和 return 完成需求。使函数成为纯函数,这样也可使代码更易于测试。


// bad
let fruits = "Apple Banana"
function splitFruits() {
  fruits = fruits.split(" ")
}
function addItemToCart(cart, item) {
  cart.push({ item, data: Date.now() })
}
// good
let fruits = "Apple Banana"
function splitFruits(fruits) {    
  return fruits.split(" ")
}
function addItemToCart(cart, item) {
  return [...cart, { item, data: Date.now() }]
}

4、整合逻辑关注点

当项目过于复杂时,常将各种逻辑混杂在一起,这不仅不利于后续扩展,还影响代码理解。因此,应尽量将相关逻辑抽离并集中管理。例如 React 中的 hooks,以及 Vue3 中的 Composition API 均采用了这一思想。


// bad
export default {
  name: 'App',
  data(){
    return {
      searchHot: [],
      searchSuggest: [],
      searchHistory: [],
    },
    mounted() {
      // todo hot
      
      // todo history
    },
    methods: {
      handleSearchSuggest(){
        // todo suggest
      },
      handleSearchHistory(){
        // todo history
      }
    }
  }
}
// good
export default {
  name: "App",
  setup() {
    let { searchHot } = useSearchHot()
    let { searchSuggest, handleSearchSuggest } = useSearchSuggest()
    let { searchHistory, handleSearchHistory } = useSearchHistory()
    return {
      searchHot,
      searchSuggest,
      searchHistory,
      handleSearchSuggest,
      handleSearchHistory,
    }
  }
}
function useSearchHot() {
  // todo hot
}
function useSearchSuggest() {
  // todo suggest
}
function useSearchHistory() {
  // todo history
}


除上述四点建议外,还有诸多提升代码健壮性的方法,例如异常处理、单元测试以及使用 TS 替换 JS 等。

最后总结一下,如何编写高质量 JavaScript 代码:

一、易阅读的代码

到此这篇关于如何编写高质量 JavaScript 代码的文章就介绍到这了,更多相关编写高质量 JavaScript 代码内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

作者最新文章