如何编写更干净、更易于维护的 JavaScript 代码

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

写在前面的话:
        
编写一流的 JavaScript 代码并非在于对最新技术趋势的盲目追逐,也并非要去构建繁杂的解决方案。本文给出了若干实用的技巧,它助力我写出了更优质的代码。倘若您产生了像“其他开发人员能否轻松读懂此代码?”“此代码能否正确地进行扩展,抑或容易被错误使用?”以及“这是否属于正确的模式?”这般的疑问,本文或许能够给予您一些颇具价值的启示。那么,让我们即刻启程!

#1:使用 Lodash

        对于那些不知道的人来说,Lodash是一个包含多功能工具的 JS 库,可以简化编码任务并使日常挑战更容易处理。从代码角度来看,它相当于瑞士军刀。如果你还没有将它纳入你的代码库,你应该这样做。Npm趋势不会撒谎:它的下载量是React 的两倍,并且还在继续攀升。

        它可以轻松处理日常编码挑战,改进您的代码。以下是它如何改进您的代码的一些示例:

提高可读性

        例如,假设您要转换如下对象:

const obj = {
  required: false,
  custom: true,
  essential: true,
  default: false,
};

        放入只有具有真值的键的数组中:[‘custom’, ‘essential']。仅使用 JS 工具,您需要想出类似以下内容的内容:

const truthyKeys = Object
   . keys (obj) 
  . filter ( key => obj[key] === true );

        当然,普通的 JavaScript 版本也可以工作,但与 Lodash 的方法相比,它的可读性稍差一些:

const keys = _.chain (obj) .pickBy ( 
  Boolean ) .keys (
   ) .   value ( ) ;

它提供了很多有用的方法

        好吧,你可能不太相信。那么合并嵌套对象怎么样?

使用 JS:

const merged = {
  ...obj1,
  ...obj2,
  nested: {
    ...obj1.nested,
    ...obj2.nested,
  },
};

使用Lodash:

const merged = _.merge({}, obj1, obj2);

        它不仅用于合并。您还可以使用 ;等来.flattenDeep展平深层嵌套的数组或按唯一项过滤数组。.uniqBy

开箱即用的防抖/节流

        防抖或节流是常见的做法,但如果您不使用 Lodash,您可能正在编写自己的函数或使用第三方软件包。既然 Lodash 有现成的依赖项,为什么还要使用新的依赖项呢?

使用 JS:

// 导入或创建自己的:

function  debounce ( func, wait ) { 
  let timeout; 
  return  function ( ...args ) { 
    clearTimeout (timeout); 
    timeout = setTimeout ( () => func.apply ( this , args), wait); 
  }; 
} 

const debouncedFunc = debounce (func, wait)

使用Lodash:

const debouncedFunc = _.debounce ( func , wait);

#2:尽量避免使用 Switch Case

        switch这些情况可能很有用,但它们也有一些缺点,使得它们与其他解决方案相比不太理想:

  • 它们很快就会变得冗长且难以阅读,
  • 容易出现诸如缺少break语句之类的错误,从而导致意外执行多个案例的失败错误,
  • 他们比其他替代方案花费了更多行来编写相同的逻辑,
  • 默认促进缺乏关注点分离(如果忘记分离,代码将在后续情况下继续失败)。

        那么,有什么更好的选择吗?使用对象或映射将数据和逻辑分开:

// instead of:
const retrieveStatus (statusCode) => {
  return switch (statusCode) {
    case 1:
      return 'Pending';
    case 2:
      return 'In Progress';
    case 3:
      return 'Completed';
    case 4:
      // Missing break statement, so default it is
    default:
      return 'Unknown';
  }
}

// do this:
const statuses = {
  1: 'Pending',
  2: 'In Progress',
  3: 'Completed',
  4: 'Failed',
  5: 'Unknown'
};

const retrieveStatus = (statusCode) => _.get(statuses, statusCode, statuses.5);

        这种方法可让您的代码更简洁、更易读,并将逻辑集中在一个地方。这样,您就无需在一个函数中浏览大量案例或条件。如果您需要查看或修改逻辑,可以检查保存备选方案的对象或映射。它更易于阅读且扩展性更好。

#3:避免if/else if声明

        从上一点继续说,您还应该避免使用if/else if块。

        使用if/else if语句很快就会变得繁琐且难以阅读,尤其是在处理许多条件时。如果您编写了多个if/else if块,请重新构造它们以使用对象或映射,就像我们对案例所做的那样switch。如您所见,object + getter 函数统治了它们。

例如,而不是:

function getStatusDescription(statusCode) {
  if (statusCode === 1) {
    return 'Pending';
  } else if (statusCode === 2) {
    return 'In Progress';
  } else if (statusCode === 3) {
    return 'Completed';
  } else if (statusCode === 4) {
    return 'Failed';
  } else {
    return 'Unknown';
  }
}

使用:

const statuses = {
  1: 'Pending',
  2: 'In Progress',
  3: 'Completed',
  4: 'Failed',
  5: 'Unknown'
};

const withdrawStatus = (statusCode) => _.get (statuses, statusCode , statuses .5 );

        是的,它与 switch 语句的块相同。

#4:不要嵌套 Ifs — Use 扁平结构

        嵌套if语句会很快使你的代码变得一团糟。不要将if块堆叠在一起,而要使用扁平结构。识别此模式的一个简单方法是检查你的代码是否看起来像箭头你可以通过查看缩进来发现这一点:每次有新的块时,代码看起来都会像箭头。每次你有箭头代码时,你都应该重构它。让我们来看一个例子:

const statusHandlers = {
pending: (order) => {
// do something
},
shipped: (order) => {
// do something
},
default: (order) => {
// do something
},
};
const paymentHandlers = {
creditCard: (order) => {
// do something
},
paypal: (order) => {
// do something
},
default: (order) => {
// do something
},
};

function processOrder(order) {
// typeguards and validations always at the top
if (!order) {
return handleInvalidOrder();
}

const { status, paymentMethod } = order;
const handleStatus = _.get(statusHandlers, status) || statusHandlers.default;
const handlePayment = _.get(paymentHandlers, paymentMethod) || paymentHandlers.default;

try {
handleStatus(order);
handlePayment(order);
} catch (e) {
return `Order failed. Reason: ${e.message}`
}

return "Order processed";
}

        看到扁平结构如何让事情变得更清晰了吗?此外,将其分解成更小的部分使测试变得轻而易举,并允许您将代码组织到不同的文件中,从而提高可读性并使一切保持整洁。

        此外,object + getter 函数再次让我们的生活更加轻松。这一次,getter 返回一个完整的函数 — 处理程序 — 而不是一个简单的原语。

#5 不要使用 let

        自 ES6 以来,引入了letconst来替代var变量定义。说实话,这很棒。但是,lets应该很少使用。同样,所有递归函数都可以用循环和显式堆栈来实现,所有let声明都可以重新定义为const

        让我们看一个例子。以下函数处理表中的排序。它接收应按其排序的字段和方向。当它收到一个新元素时,如果其fieldName不同,它应该将排序重置direction为后代,无论它在该字段上收到什么有效载荷。但如果相同fieldname,则应使用新的排序方向:

type Direction: 'asc' | 'desc'

type SortOptions {
  fieldName: string;
  direction: Direction;
}

const sortList = (
  { newOptions, currentOptions }: { newOptions: SortOptions, currentOptions: SortOptions }
): SortOptions => {
  let sortingDirection: Direction;

  if (currentOptions.fieldName !== newOptions.fieldName) {
    sortingDirection = 'desc';
  } else {
    sortingDirection = newOptions.direction;
  }

  return {
    fieldName: newOptions.fieldName,
    direction: sortingDirection,
  };
};

        没啥不好,对吧?但是如果我们删除声明let并直接使用if/else呢?

type Direction = 'asc' | 'desc';

interface SortOptions {
  fieldName: string;
  direction: Direction;
}

const sortList = (
  { newOptions, currentOptions }: { newOptions: SortOptions, currentOptions: SortOptions }
): SortOptions => {

  const newDirection = currentFieldName !== newFieldName ? 'desc' : newOptions.direction

  return {
    fieldName: newOptions.fieldName,
    direction: newDirection,
  };
};

        有时,您可以使用其他语法(例如三元运算符)重写它。在其他情况下,您需要将其外包给另一个函数。但这不是很棒吗?如果您将其拆分为另一个函数,您的代码将更好地遵循 SRP 原则,并且变得更易于测试、阅读和维护。

#6 不要使用 else,而要尽早返回

        使用else没什么问题,但尽早returns可以使您的代码更清晰、更易读。尽早返回有助于避免嵌套条件并使其更易于理解,从而降低代码的复杂性。让我们看一个例子:

const calculateResult = (input: number): number => {
  let result: number;

  if (input > 10) {
    result = input * 2;
  } else {
    result = input + 5;
  }

  return result;
};

        使用提前返回后:

const calculateResult = (input: number): number => {
  if (input > 10) {
    return input * 2;
  }

  return input + 5;
};

没什么问题早期返回允许您提前处理边缘情况或特殊条件,从而使主要逻辑更简洁、更集中。

Wrapping up

        每当你看到:

  • switch cases,
  • if / else if blocks,
  • let declarations,
  • else blocks,

        它表示代码可以重构为更简单、更小的函数。保持代码整洁;这样可以帮助队友更好地理解你的代码。另外,别忘了使用 Lodash。它非常有用,可以避免手写大量“辅助”函数。

        Happy coding!


本文内容仅仅是技术探讨和学习,并不构成任何投资建议。
转发请注明原作者和出处。

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老余捞鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值