千家信息网

怎样编写高质量JavaScript代码

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,怎样编写高质量JavaScript代码,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、易阅读的代码首先说一下,代码是写给自己或团队
千家信息网最后更新 2025年01月19日怎样编写高质量JavaScript代码

怎样编写高质量JavaScript代码,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

一、易阅读的代码

首先说一下,代码是写给自己或团队成员看的,良好的阅读方式是编写高质量代码的前提条件。这里总结了四点具体操作方式分享给大家。

1、统一代码格式

不要一会这样写,一会那样写,尽量统一写法,下面举例。

// badfunction foo(x,y) {  return {    sum : x + y  };}function bar(m, n){  let ret = m*n  return ret;}// goodfunction 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"等以数字直接写出的值)。虽然程式作者写的时候自己能了解数值的意义,但对其他程式员而言,甚至作者本人经过一段时间后,都会很难理解这个数值的用途。

// badsetTimeout(blastOff, 86400000)_document.onkeydown = function (ev) {  if (ev.keyCode === 13) {    // todos  }}// goodconst MILLISECONDS_IN_A_DAY = 86400000const ENTER_KEY = 13setTimeout(blastOff, MILLISECONDS_IN_A_DAY)_document.onkeydown = function (ev) {  if (ev.keyCode === ENTER_KEY) {    // todos  }}

当然还有魔术字符串也是像上面一样去处理,上面代码中的常量命名推荐采用下划线命名的方式,其他如变量、函数等推荐用驼峰进行命名。

其实减少this的使用频率也是一样的道理,当代码中充斥着大量this的时候,我们往往很难知道它是谁,需要花费很多时间进行阅读。

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

3、单一功能原则

无论是编写模块、类、还是函数都应该让他们各自都只有单一的功能,不要让他们做过多的事情,这样阅读起来会非常简单,扩展起来也会非常灵活。

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

4、减少嵌套层级

多层级的嵌套,如:条件嵌套、循环嵌套、回调嵌套等,对于代码阅读是非常不利的,所以应尽量减少嵌套的层级。

像解决条件嵌套的问题,一般可采用卫语句(guard clause)的方式提前返回,从而减少嵌套。

// badfunction foo() {  let result  if (isDead) {    result = deadAmount()  } else {    if (isRet) {      result = retAmount()    } else {      result = normalAmount()    }  }  return result}// goodfunction foo() {  if (isDead) {    return deadAmount()  }  if (isRet) {    return retAmount()  }  return normalAmount()}

除了卫语句外,通过还可以采用短路运算、条件运算符等进行条件语句的改写。

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

像解决回调嵌套的问题,一般可采用"async/await"方式进行改写。

// badlet 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()// goodlet fs = require("fs").promisesasync 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、优化算法

递归是一种常见的算法,下面是用递归实现的"求阶乘"的操作。

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

"尾调用"是一种可以重用栈帧的内存管理优化机制,即外部函数的返回值是一个内部函数的返回值。

2、使用内置方法

很多功能都可以采用JavaScript内置方法来解决,往往内置方法的底层实现是最优的,并且内置方法可在解释器中提前执行,所以执行效率非常高。

下面举例为:获取对象属性和值的复合数组形式。

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

3、减少作用域链查找

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

概念太复杂的话, 看下面这样一张图。

作用域链这个链表就是 3(头部:bar) -> 2(foo) -> 1(尾部:全局),所以查找变量的时候,应尽量在头部完成获取,这样就可以节省性能,具体对比如下。

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

除了减少作用域链查找外,减少对象属性的查找也是一样的道理。

// badfunction isNull(arg) {  return Object.prototype.toString.call(arg) === "[object Null]"}function isFunction(arg) {  return Object.prototype.toString.call(arg) === "[object Function]"}// goodlet toString = Object.prototype.toStringfunction isNull(arg) {  return toString.call(arg) === "[object Null]"}function isFunction(arg) {  return toString.call(arg) === "[object Function]"}

4、避免做重复的代码

有时候编写程序时,会出现很多重复执行的代码,最好要避免做重复操作。先举一个简单的例子,通过循环找到第一个满足条件元素的索引位置。

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

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

// badfunction foo(n) {  if (n < 3) {    return 1  }  return foo(n - 1) + foo(n - 2)}foo(40)     // 平均耗时:1043ms// goodlet 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、使用新语法

很多新语法可弥补之前语法的BUG,让代码更加健壮,应对未来。

// badvar a = 1isNaN(NaN)              // trueisNaN(undefined)        // true// goodlet a = 1Number.isNaN(NaN)       // trueNumber.isNaN(undefined) // false

新语法还可以简化之前的操作,让代码结构更加清晰。

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

2、随时可扩展

由于产品需求总是会有新的变更,对软件的可扩展能力提出了很高要求,所以健壮的代码都是可以随时做出调整的代码。

// badfunction foo(animal) {  if (animal === "dog" || animal === "cat") {    // todos  }}function bar(name, age) {}bar("james", 36)// goodfunction 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完成需求。让函数成为一种纯函数,这样也可使代码更容易被测试。

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

4、整合逻辑关注点

当项目过于复杂的时候,经常会把各种逻辑混在一起,对后续扩展非常不利,而且还影响对代码的理解。所以尽量把相关的逻辑抽离到一起,进行集中式的管理。像React中的hooksVue3中的Composition API都是采用这样的思想。

// badexport default {  name: 'App',  data(){    return {      searchHot: [],      searchSuggest: [],      searchHistory: [],    },    mounted() {      // todo hot            // todo history    },    methods: {      handleSearchSuggest(){        // todo suggest      },      handleSearchHistory(){        // todo history      }    }  }}// goodexport 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代码问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

代码 作用 函数 对象 方式 条件 变量 方法 高质量 健壮 全局 语法 问题 副作用 就是 建议 性能 拷贝 时候 程式 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全专业色弱有什么影响 服务器压测工具 软件开发公司招商专员 伴伴网络技术 空间数据库矢量查询 多媒体软件开发的基本流程 海思机顶盒微型服务器 周期性的软件开发模型 天猫国际贸易数据库 旅游软件开发的备选方案 数据库删除函数语法错误 服务器去毛刺视频教程 超级计算机是小型服务器组成的吗 御2行业进阶版无法连接服务器 专升本计算机网络技术 六安求职招聘软件开发定制公司 三一重机智能软件开发工程师 数据库索引的连接类型 中兴最厉害的数据库 张家口聊天软件开发专业定制 网络安全和防诈骗的手抄报 帮我播放网络安全的手抄报 一般网络安全设备需要哪些配置 福建管理系统软件开发价格 ctf网络安全大赛电视剧 宝山区网络营销网络技术备案 什么是分布式数据库 济南云畅网络技术有限公司 浙江正规软件开发报价 sql文本装入数据库
0