让你的函数调用更易读。
介绍
“事实上,阅读时间与写作时间的比例远超 10:1。我们不断地阅读旧代码,以此来编写新代码。……[因此,]让代码易于阅读,就能让编写变得更容易。”——罗伯特·C·马丁,《代码整洁之道:敏捷软件开发手册》
编写易于理解、修改和扩展的代码是软件开发人员的理想目标。但这很难。难到什么程度呢?已经有很多书籍探讨过这个主题,世界各地的人们也经常在各种会议和聚会上就此发表演讲。
如果你编写或阅读过大量的 JavaScript 代码,你可能已经见过类似这样的函数调用:
getUnicorns(10, 5, 10, true);
getUnicorns(7);
这段代码很难读懂。这些数字和布尔值是什么意思?
这些函数是在实践中逐渐形成的。也许它最初只是一个非常简单的函数,随着需求的增加,人们就一个接一个地添加参数,最终导致新用户在查看代码时感到非常困惑。又或许它最初只有四个参数,但编写并立即调用它的人当时很清楚自己想要实现什么功能,并且一切都记忆犹新。
无论事情是如何发生的,现在都存在问题。将来,其他人(甚至是未来的你)会遇到这段代码,为了理解它的运行机制,他们需要进行大量的研究,并且要记住很多细节。所有这些都会分散他们的注意力,使他们无法专注于编写高质量的代码。
难以理解的代码会吸引难以理解的代码(以及漏洞🐜)。
Python 是如何做到的?使用命名参数
学习其他语言是提升我们开发水平的绝佳途径。同时,编写代码时坚持使用惯用的语言风格也至关重要。因此,这是一种巧妙的平衡。
在 Python 中,有一种称为命名参数(也称为关键字参数)的机制,指的是在调用函数时必须命名的参数。
def get_unicorns(amount, min_age = 0, max_age = 100, only_magical = false):
pass
get_unicorns(10, min_age=5, max_age=10, only_magical=true)
阅读起来容易多了!
让我们改进我们的JS代码
遗憾的是,JavaScript 中没有命名参数。但是,借助ES6 的对象解构,我们可以实现类似的功能。
对象解构的简要介绍
/* Extract properties you're interested in */
const { name, age } = unicorn
/* is the same as */
const name = unicorn.name
const age = unicorn.age
/* ----- */
/* Give default values to destructured properties */
const { name, age = 5 } = unicorn
/* which is same as */
const name = unicorn.name
const age = unicorn.age !== undefined ? unicorn.age : 5
/* ----- */
/* Give default value to function parameter */
function getUnicorns(amount, options = {})
Javascript 中的“命名参数”
利用我们对对象解构的知识,让我们使原始代码更具可读性。
function getUnicorns(amount, {minAge = 0, maxAge = 99, onlyMagical = false} = {}) {}
getUnicorns(10, {minAge: 5, maxAge: 10, onlyMagical: true});
getUnicorns(7);
getUnicorns(5, {onlyMagical: true});
就像我们在 Python 示例中看到的那样,阅读这样的代码会更加愉快,我们可以专注于重要的部分:逻辑和目标。
除了提高可读性之外,这种对象解构方法的另一个好处是,你不必担心参数的顺序,甚至不必担心它们是否存在。
如果您觉得函数定义中的解构过于繁杂,尤其是当存在默认值时,您可以将其提取到函数体中:
function getUnicorns(amount, opts = {}) {
const {
minAge = 0,
maxAge = 99,
onlyMagical = false
} = opts
/* rest of the function body here */
}
前一个例子的一个缺点是,你会失去对编辑器内联自动完成或文档中默认值和可用选项的可见性。
使用 TypeScript 进一步改进
如果你使用 TypeScript,这种方法可以让你获得更多收益。
type UnicornFilter = {
minAge?: Number;
maxAge?: Number;
onlyMagical?: Boolean;
}
function getUnicorns(
amount: Number,
{minAge = 0, maxAge = 99, onlyMagical = false}: UnicornFilter = {},
) {}
getUnicorns(10, {minAge: 5, maxAge: 10, onlyMagical: true});
getUnicorns(7);
getUnicorns(5, {onlyMagical: true});
通过将类型定义为UnicornFilter,我们可以获得以下几点:
- 你不会最终使用不应该存在的参数来调用函数。
- 你为参数传入了正确的值。
- 编辑器中的自动补全功能会变得更好,因为它知道接下来会发生什么。
结论
在编写代码时,保持代码的可读性至关重要。通常情况下,代码会因为小的改动日积月累而变得难以阅读。如果一开始代码很简单,然后被要求“添加这个功能”,你未必会考虑到长期的可读性问题。
当你在代码中添加少量功能时,要勇于重构。趁着代码及其功能还记忆犹新的时候进行重构总是最容易的,而不是等到六个月后新团队成员查看代码并试图弄明白它的作用时才去做。
文章来源:https://dev.to/hamatti/make-your-function-calls-more-read-12n1