高端的食材只需要最朴素的烹饪方式 - 基于 ES6 的重构屎山中的一坨

前言

随着最近在群里看代码(摸🐟)次数越来越多,越来越感觉很多人都在写 一次性代码

什么是一次性代码?

一次性代码从广义上讲,是只使用一次的代码。如:数据库迁移脚本、临时活动页面、kpi工程

但我指的一次性代码更倾向于写出来就 很难维护 的代码。

举个栗子:

我们在工作时会发现,大佬们写一次性代码也尤其优雅,而新手在写真正需要维护的代码时,也写的像依托答辩。

这是每个新码农的必经之路,我在带新人时,常常会想,这条路为什么在不同人之间,具现的长度不同?大家会发现,大厂的员工往往比小厂的员工成长的更快。

最后经我总结,导致 「路」 长短的原因有以下几点:

  • 接触到的代码质量不同
  • 代码审核不严格
  • 没有重构思维

虽然我写过一篇 《重构「屎山」?你可能永远也不该做这件事!》,但你如果仔细看下来就会发现,我想表达的并不是不要重构(虽然评论区很多人曲解了我的意思),而是如何重构。

正文

问题

今天在群里有朋友发自己的工作状态,一眼就看到了这个经典(奇葩)的代码。

问题定位

我这里拿了比较经典的例子,这个代码的问题一眼就能看出来,是 三元嵌套 问题。

要谈 三元嵌套if else嵌套 的问题,那么一定避不开的一张图就是经典的元气弹代码。

这样的代码最大的问题在于,无法准确的看出具体的执行逻辑是什么。往往阅读者需要抽丝剥茧的捋清代码逻辑,在定位具体的调整点。

解决思路

由于代码是别人的图片,我们这里先转换成伪代码,可以不必纠结变量名问题(我们此次不涉及变量、函数命名)。

matlab 复制代码
function getSxClass() {
  return function (sxData) {
    var type =
      sxData.specialCase & sxData.specialcase.length > 0 ? 'onShaixuan'
        : sxData.screening & sxData.screening.length > 0 ? 'onShaixuan'
          : sxData.publicSecurity & sxData.publicSecurity.length > 0 ? 'onShaixuan'
            : sxData.peoplelive & sxData.peoplelive.length > 0 ? 'onShaixuan'
              : sxData.professionalrdenlevel & sxData.professionalrdenlevel.length > 0 ? 'onShaixuan'
                : sxData.remediation & sxData.remediation.length > 0 ? 'onShaixuan'
                  : sxData.dhcity & sxData.dhcity != '' ? 'onShaixuan'
                    : sxData.propertyattribute & sxData.propertyattribute != '' ? 'onShaixuan'
                      : sxData.housinguse & sxData.housinguse != '' ? 'onShaixuan'
                        : sxData.houserusefor & sxData.houserusefor != '' ? 'onShaixuan'
                          : sxData.structureType & sxData.structureType != '' ? 'onShaixuan'
                            : '';

    return type
  }
}

三元转换if

我们都知道,在简单判断的情况下,用 三元运算符 是好的选择。

但是在涉及到多层判断时,嵌套的三元表达式,会导致代码可读性降低。

我们首先把 三元表达式 转换 if else ,再进行重构会轻松的多。

因此,我们第一步将 三元表达式 转换为 if else

matlab 复制代码
function getSxClass() {
  return function (sxData) {
    var type = ""
    if (sxData.specialCase & sxData.specialcase.length > 0) {
      type = 'onShaixuan'
    } else {
      if (sxData.screening & sxData.screening.length > 0) {
        type = 'onShaixuan'
      } else {
        if (sxData.publicSecurity & sxData.publicSecurity.length > 0) {
          type = 'onShaixuan'
        } else {
          if (sxData.peoplelive & sxData.peoplelive.length > 0) {
            type = 'onShaixuan'
          } else {
            if (sxData.professionalrdenlevel & sxData.professionalrdenlevel.length > 0) {
              type = 'onShaixuan'
            } else {
              if (sxData.remediation & sxData.remediation.length > 0) {
                type = 'onShaixuan'
              } else {
                if (sxData.dhcity & sxData.dhcity != '') {
                  type = 'onShaixuan'
                } else {
                  if (sxData.propertyattribute & sxData.propertyattribute != '') {
                    type = 'onShaixuan'
                  } else {
                    if (sxData.housinguse & sxData.housinguse != '') {
                      type = 'onShaixuan'
                    } else {
                      if (sxData.houserusefor & sxData.houserusefor != '') {
                        type = 'onShaixuan'
                      } else {
                        if (sxData.structureType & sxData.structureType != '') {
                          type = 'onShaixuan'
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    return type
  }
}

卫语句优化if

优化完我们会发现,这不就是元气弹问题吗!!我们的问题看着是 三元嵌套 问题,但转换为 if else 时,就会看出,明显是元气弹问题。

那下面我们就这元气弹问题谈谈元气弹的解决方案。元气弹代码有经典的解决方案就是 卫语句 ,那么我们接下来通过卫语句的方式,解决元气弹代码。

kotlin 复制代码
function getSxClass() {
  return function (sxData) {
    if (sxData.specialCase & sxData.specialcase.length > 0) {
      return 'onShaixuan'
    }
    if (sxData.screening & sxData.screening.length > 0) {
      return 'onShaixuan'
    }
    if (sxData.publicSecurity & sxData.publicSecurity.length > 0) {
      return 'onShaixuan'
    }
    if (sxData.peoplelive & sxData.peoplelive.length > 0) {
      return 'onShaixuan'
    }
    if (sxData.professionalrdenlevel & sxData.professionalrdenlevel.length > 0) {
      return 'onShaixuan'
    }
    if (sxData.remediation & sxData.remediation.length > 0) {
      return 'onShaixuan'
    }
    if (sxData.dhcity & sxData.dhcity != '') {
      return 'onShaixuan'
    }
    if (sxData.propertyattribute & sxData.propertyattribute != '') {
      return 'onShaixuan'
    }
    if (sxData.housinguse & sxData.housinguse != '') {
      return 'onShaixuan'
    }
    if (sxData.houserusefor & sxData.houserusefor != '') {
      return 'onShaixuan'
    }
    if (sxData.structureType & sxData.structureType != '') {
      return 'onShaixuan'
    }

    return ''
  }
}

这样我们的代码逻辑就清晰多了,只有清晰的代码逻辑,才能方便我们开发维护。

同时,我们也能更好的重构代码。

if 合并

那么基于我们的卫语句代码,我们可以看出,很多的条件判断,最后都指向同一件事情。

那么我们可以将条件判断合并,这样减少无用的代码。

matlab 复制代码
function getSxClass() {
  return function (sxData) {
    if (
      sxData.specialCase && (sxData.specialcase.length > 0) ||
      sxData.screening && (sxData.screening.length > 0) ||
      sxData.publicSecurity && (sxData.publicSecurity.length > 0) ||
      sxData.peoplelive && (sxData.peoplelive.length > 0) ||
      sxData.professionalrdenlevel && (sxData.professionalrdenlevel.length > 0) ||
      sxData.remediation && (sxData.remediation.length > 0) ||
      sxData.dhcity && (sxData.dhcity != "") ||
      sxData.propertyattribute && (sxData.propertyattribute != "") ||
      sxData.housinguse && (sxData.housinguse != "") ||
      sxData.houserusefor && (sxData.houserusefor != "") ||
      sxData.structureType && (sxData.structureType != "")
    ) {
      return "onShaixuan";
    }

    return ''
  }
}

实际上,通常工作中,重构到这一步就基本满足需求了。

但我们的重构教程不一样,我们要做就一定尽可能的做到最好。

那么,请接着往下看。

reduce 重构

我们通过上面的重构代码可以看出,函数实际上就做了一件事儿:当指定元素为空时,返回 onShaixuan,否则返回 ''

同时,我们有两种需要判断是否为空的方式,一种是 空字符串 ,一种是 空数组

那么我们通过 reduce 函数,默认返回 '' ,在遇到为空的数组、字符串时,改变返回为 onShaixuan

javascript 复制代码
function A(obj) {
  const needVerifyArr = ['specialCase', 'screening', 'publicSecurity', 'peoplelive', 'professionalrdenlevel', 'remediation']
  const needVerifyStr = ['dhcity', 'propertyattribute', 'housinguse', 'houserusefor', 'structureType']
  const arrVerifyRes = needVerifyArr.reduce((r, v) => obj[v] && obj[v].length > 0 ? 'onShaixuan' : r, '')
  return needVerifyStr.reduce((r, v) => obj[v] && obj[v] !== "" ? 'onShaixuan' : r, arrVerifyRes)
}

奥,代码优雅起来了,那我们有没有办法更优雅一点呢?

提取空数组、字符串判断函数

那么再看下去,显而易见的,判断空时的代码 xx && xx.length > 0 ,不能一眼看出要干什么。

那么,抽!

javascript 复制代码
function A(obj) {
  function isEmptyArr(arr) {
    return arr && arr.length < 1
  }

  function isEmptyStr(str) {
    return str && str === ""
  }
  const needVerifyArr = ['specialCase', 'screening', 'publicSecurity', 'peoplelive', 'professionalrdenlevel', 'remediation']
  const needVerifyStr = ['dhcity', 'propertyattribute', 'housinguse', 'houserusefor', 'structureType']
  const arrVerifyRes = needVerifyArr.reduce((r, v) => isEmptyArr(obj[v]) ? 'onShaixuan' : r, '')
  return needVerifyStr.reduce((r, v) => isEmptyStr(obj[v]) ? 'onShaixuan' : r, arrVerifyRes)
}

哎呦,一目了然,并且我们后期写的空字符串、数组判断有问题时,也更好维护。

那我们到这步就结束了吗?

两个全空判断 + 一个三元返回

javascript 复制代码
function getSxClass(obj) {
  const needVerifyArr = ['specialCase', 'screening', 'publicSecurity', 'peoplelive', 'professionalrdenlevel', 'remediation']
  const needVerifyStr = ['dhcity', 'propertyattribute', 'housinguse', 'houserusefor', 'structureType']
  const isAllEmptyArr = needVerifyArr.reduce((r, v) => isEmptyArr(obj[v]) && r, true)
  const isAllEmptyStr = needVerifyStr.reduce((r, v) => isEmptyStr(obj[v]) && r, true)
  return isAllEmptyArr && isAllEmptyStr ? '' : 'onShaixuan'
}

最后

结语

通过上面一个问题,我们可以学习到多个知识点,包括但不限于 ES6 reduce、卫语句、非空方法验证。

我们应当习惯性的进行代码重构,当好的开发方式形成我们的开发习惯后,凭借本能,我们也能写出优雅的代码。

附录

卫语句

卫语句既是一种编程模式,也是我们优化 if else 的常见手段。一般用来处理特殊的异常情况,以便于主函数逻辑可以保持简洁、清晰。最常用卫语句的是后台处理异常。

例如,假设我们有一个函数,它需要一个非空的字符串作为参数。我们可以使用卫语句来检查这个条件:

javascript 复制代码
function processString(str) {
  // 卫语句
  if (!str || typeof str !== 'string') {
    throw new Error('Invalid argument: str must be a non-empty string');
  }

  // 主要的函数逻辑
  // ...
}

该方案通常后端用的比较多,但我们前端做验证时,常常也是一样的逻辑。因此,我们学习、使用对我们的开发也是好处多多~

reduce

JS 中 reduce 方法是 ES6 新增的一个高阶函数,但很多人容易忽略这个方法,因为它往往没那么直观的感受到什么时候我们该使用它。

这里只简单讲讲它的用法:

我们需要计算一个子数组中所有数字的和:

js 复制代码
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => (accumulator + currentValue), 0);

后续我会总结一篇 ES6 中的新数组方法的意义、使用方法、实际案例,欢迎大家关注后续内容~~

相关推荐
算法歌者17 分钟前
[算法]入门1.矩阵转置
算法
林开落L31 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色32 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
tyler_download34 分钟前
手撸 chatgpt 大模型:简述 LLM 的架构,算法和训练流程
算法·chatgpt
SoraLuna1 小时前
「Mac玩转仓颉内测版7」入门篇7 - Cangjie控制结构(下)
算法·macos·动态规划·cangjie
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
鸽鸽程序猿1 小时前
【算法】【优选算法】前缀和(上)
java·算法·前缀和
九圣残炎1 小时前
【从零开始的LeetCode-算法】2559. 统计范围内的元音字符串数
java·算法·leetcode
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf