【JavaScript从入门到入神】代码风格|注释|代码质量

举报
xcc-2022 发表于 2022/08/16 11:22:05 2022/08/16
【摘要】 @[toc] 代码风格我们的代码必须尽可能的清晰和易读。这实际上是一种编程艺术 —— 以一种正确并且人们易读的方式编码来完成一个复杂的任务。一个良好的代码风格大大有助于实现这一点。 语法下面是一个备忘单,其中列出了一些建议的规则(详情请参阅下文):现在,让我们详细讨论一下这些规则和它们的原因吧。⚠️没有什么规则是“必须”的没有什么规则是“刻在石头上”的。这些是风格偏好,而不是宗教教条。 花括...

@[toc]

代码风格

我们的代码必须尽可能的清晰和易读。

这实际上是一种编程艺术 —— 以一种正确并且人们易读的方式编码来完成一个复杂的任务。一个良好的代码风格大大有助于实现这一点。

语法

下面是一个备忘单,其中列出了一些建议的规则(详情请参阅下文):
在这里插入图片描述

现在,让我们详细讨论一下这些规则和它们的原因吧。

⚠️没有什么规则是“必须”的

没有什么规则是“刻在石头上”的。这些是风格偏好,而不是宗教教条。

花括号

在大多数的 JavaScript 项目中,花括号以 “Egyptian” 风格(译注:“egyptian” 风格又称 K&R 风格 — 代码段的开括号位于一行的末尾,而不是另起一行的风格)书写,左花括号与相应的关键词在同一行上 — 而不是新起一行。左括号前还应该有一个空格,如下所示:

if (condition) {
  // do this
  // ...and that
  // ...and that
}

单行构造(如 if (condition) doSomething())也是一个重要的用例。我们是否应该使用花括号?如果是,那么在哪里?

下面是这几种情况的注释,你可以自己判断一下它们的可读性:

  1. 😠 初学者常这样写。非常不好!这里不需要花括号:

    if (n < 0) {alert(`Power ${n} is not supported`);}
    
  2. 😠 拆分为单独的行,不带花括号。永远不要这样做,添加新行很容易出错:

    if (n < 0)
      alert(`Power ${n} is not supported`);
    
  3. 😏 写成一行,不带花括号 — 如果短的话,也是可以的:

    if (n < 0) alert(`Power ${n} is not supported`);
    
  4. 😃 最好的方式:

    if (n < 0) {
      alert(`Power ${n} is not supported`);
    }
    

对于很短的代码,写成一行是可以接受的:例如 if (cond) return null。但是代码块(最后一个示例)通常更具可读性。

行的长度

没有人喜欢读一长串代码,最好将代码分割一下。

例如:

// 回勾引号 ` 允许将字符串拆分为多行
let str = `
  ECMA International's TC39 is a group of JavaScript developers,
  implementers, academics, and more, collaborating with the community
  to maintain and evolve the definition of JavaScript.
`;

对于 if 语句:

if (
  id === 123 &&
  moonPhase === 'Waning Gibbous' &&
  zodiacSign === 'Libra'
) {
  letTheSorceryBegin();
}

一行代码的最大长度应该在团队层面上达成一致。通常是 80 或 120 个字符。

缩进

有两种类型的缩进:

  • 水平方向上的缩进:2 或 4 个空格。

    一个水平缩进通常由 2 或 4 个空格或者 “Tab” 制表符(Tab 键)构成。选择哪一个方式是一场古老的圣战。如今空格更普遍一点。

    选择空格而不是 tabs 的优点之一是,这允许你做出比 “Tab” 制表符更加灵活的缩进配置。

    例如,我们可以将参数与左括号对齐,像下面这样:

    show(parameters,
         aligned, // 左边有 5 个空格
         one,
         after,
         another
      ) {
      // ...
    }
    
  • 垂直方向上的缩进:用于将代码拆分成逻辑块的空行。

    即使是单个函数通常也被分割为数个逻辑块。在下面的示例中,初始化的变量、主循环结构和返回值都被垂直分割了:

    function pow(x, n) {
      let result = 1;
      //              <--
      for (let i = 0; i < n; i++) {
        result *= x;
      }
      //              <--
      return result;
    }
    

    插入一个额外的空行有助于使代码更具可读性。写代码时,不应该出现连续超过 9 行都没有被垂直分割的代码。

分号

每一个语句后面都应该有一个分号。即使它可以被跳过。

有一些编程语言的分号确实是可选的,那些语言中也很少使用分号。但是在 JavaScript 中,极少数情况下,换行符有时不会被解释为分号,这时代码就容易出错。更多内容请参阅 【代码结构】一章的内容。

如果你是一个有经验的 JavaScript 程序员,你可以选择像 StandardJS 这样的无分号的代码风格。否则,最好使用分号以避免可能出现的陷阱。大多数开发人员都应该使用分号。

嵌套的层级

尽量避免代码嵌套层级过深。

例如,在循环中,有时候使用 [continue](continue(计算机语言)_百度百科 (baidu.com)) 指令以避免额外的嵌套是一个好主意。

例如,不应该像下面这样添加嵌套的 if 条件:

for (let i = 0; i < 10; i++) {
  if (cond) {
    ... // <- 又一层嵌套
  }
}

我们可以这样写:

for (let i = 0; i < 10; i++) {
  if (!cond) continue;
  ...  // <- 没有额外的嵌套
}

使用 if/elsereturn 也可以做类似的事情。

例如,下面的两个结构是相同的。

第一个:

function pow(x, n) {
  if (n < 0) {
    alert("Negative 'n' not supported");
  } else {
    let result = 1;

    for (let i = 0; i < n; i++) {
      result *= x;
    }

    return result;
  }
}

第二个:

function pow(x, n) {
  if (n < 0) {
    alert("Negative 'n' not supported");
    return;
  }

  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }

  return result;
}

但是第二个更具可读性,因为 n < 0 这个“特殊情况”在一开始就被处理了。一旦条件通过检查,代码执行就可以进入到“主”代码流,而不需要额外的嵌套。

函数位置

如果你正在写几个“辅助”函数和一些使用它们的代码,那么有三种方式来组织这些函数。

  1. 在调用这些函数的代码的 上方 声明这些函数:

    // 函数声明
    function createElement() {
      ...
    }
    
    function setHandler(elem) {
      ...
    }
    
    function walkAround() {
      ...
    }
    
    // 调用函数的代码
    let elem = createElement();
    setHandler(elem);
    walkAround();
    
  2. 先写调用代码,再写函数

    // 调用函数的代码
    let elem = createElement();
    setHandler(elem);
    walkAround();
    
    // --- 辅助函数 ---
    function createElement() {
      ...
    }
    
    function setHandler(elem) {
      ...
    }
    
    function walkAround() {
      ...
    }
    
  3. 混合:在第一次使用一个函数时,对该函数进行声明。

大多数情况下,第二种方式更好。

这是因为阅读代码时,我们首先想要知道的是“它做了什么”。如果代码先行,那么在整个程序的最开始就展示出了这些信息。之后,可能我们就不需要阅读这些函数了,尤其是它们的名字清晰地展示出了它们的功能的时候。

风格指南

风格指南包含了“如何编写”代码的通用规则,例如:使用哪个引号、用多少空格来缩进、一行代码最大长度等非常多的细节。

当团队中的所有成员都使用相同的风格指南时,代码看起来将是统一的。无论是团队中谁写的,都是一样的风格。

当然,一个团队可以制定他们自己的风格指南,但是没必要这样做。现在已经有了很多制定好的代码风格指南可供选择。

一些受欢迎的选择:

如果你是一个初学者,你可以从本章中上面的内容开始。然后你可以浏览其他风格指南,并选择一个你最喜欢的。

自动检查器

检查器(Linters)是可以自动检查代码样式,并提出改进建议的工具。

它们的妙处在于进行代码风格检查时,还可以发现一些代码错误,例如变量或函数名中的错别字。因此,即使你不想坚持某一种特定的代码风格,我也建议你安装一个检查器。

下面是一些最出名的代码检查工具:

  • JSLint — 第一批检查器之一。
  • JSHint — 比 JSLint 多了更多设置。
  • ESLint — 应该是最新的一个。

它们都能够做好代码检查。我使用的是 ESLint

大多数检查器都可以与编辑器集成在一起:只需在编辑器中启用插件并配置代码风格即可。

例如,要使用 ESLint 你应该这样做:

  1. 安装 Node.JS
  2. 使用 npm install -g eslint 命令(npm 是一个 JavaScript 包安装工具)安装 ESLint。
  3. 在你的 JavaScript 项目的根目录(包含该项目的所有文件的那个文件夹)创建一个名为 .eslintrc 的配置文件。
  4. 在集成了 ESLint 的编辑器中安装/启用插件。大多数编辑器都有这个选项。

下面是一个 .eslintrc 文件的例子:

{
  "extends": "eslint:recommended",
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "rules": {
    "no-console": 0,
    "indent": 2
  }
}

这里的 "extends" 指令表示我们是基于 “eslint:recommended” 的设置项而进行设置的。之后,我们制定我们自己的规则。

你也可以从网上下载风格规则集并进行扩展。有关安装的更多详细信息,请参见 https://eslint.org/docs/user-guide/getting-started。

此外,某些 IDE 有内建的检查器,这非常方便,但是不像 ESLint 那样可自定义。

总结

本章描述的(和提到的代码风格指南中的)所有语法规则,都旨在帮助你提高代码可读性。它们都是值得商榷的。

当我们思考如何写“更好”的代码的时候,我们应该问自己的问题是:“什么可以让代码可读性更高,更容易被理解?”和“什么可以帮助我们避免错误?”这些是我们讨论和选择代码风格时要牢记的主要原则。

阅读流行的代码风格指南,可以帮助你了解有关代码风格的变化趋势和最佳实践的最新想法。

✅任务

不好的风格

重要程度:four:

下面的代码风格有什么问题?

function pow(x,n)
{
  let result=1;
  for(let i=0;i<n;i++) {result*=x;}
  return result;
}

let x=prompt("x?",''), n=prompt("n?",'')
if (n<=0)
{
  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
}
else
{
  alert(pow(x,n))
}

修复它。

解决方案

你可以注意到以下几点:

function pow(x,n)  // <- 参数之间没有空格
{  // <- 花括号独占了一行
let result=1;   // <- = 号两边没有空格
for(let i=0;i<n;i++) {result*=x;}   // <- 没有空格
// { ... } 里面的内容应该在新的一行上
return result;
}

let x=prompt("x?",''), n=prompt("n?",'') // <-- 从技术的角度来看是可以的,
// 但是拆分成 2 行会更好,并且这里也缺了空格和分号 ;
if (n<=0)  // <- (n <= 0) 里面没有空格,并且应该在本行上面加一个空行
{   // <- 花括号独占了一行
// 下面的一行代码太长了,可以将其拆分成 2 行以提高可读性
alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
}
else // <- 可以像 "} else {" 这样写在一行上
{
alert(pow(x,n))  // 缺失了空格和分号 ;
}

修改后的版本:

function pow(x, n) {
let result = 1;

for (let i = 0; i < n; i++) {
 result *= x;
}

return result;
}

let x = prompt("x?", "");
let n = prompt("n?", "");

if (n <= 0) {
alert(`Power ${n} is not supported,
 please enter an integer number greater than zero`);
} else {
alert( pow(x, n) );
}

注释

正如我们在 【代码结构】一章所了解到的那样,注释可以是以 // 开始的单行注释,也可以是 /* ... */ 结构的多行注释。

我们通常通过注释来描述代码怎样工作和为什么这样工作。

乍一看,写注释可能很简单,但初学者在编程的时候,经常错误地使用注释。

糟糕的注释

新手倾向于使用注释来解释“代码中发生了什么”。就像这样:

// 这里的代码会先做这件事(……)然后做那件事(……)
// ……谁知道还有什么……
very;
complex;
code;

但在好的代码中,这种“解释性”注释的数量应该是最少的。严格地说,就算没有它们,代码也应该很容易理解。

关于这一点有一个很棒的原则:“如果代码不够清晰以至于需要一个注释,那么或许它应该被重写。”

配方:分解函数

有时候,用一个函数来代替一个代码片段是更好的,就像这样:

function showPrimes(n) {
  nextPrime:
  for (let i = 2; i < n; i++) {

    // 检测 i 是否是一个质数(素数)
    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert(i);
  }
}

更好的变体,使用一个分解出来的函数 isPrime

function showPrimes(n) {

  for (let i = 2; i < n; i++) {
    if (!isPrime(i)) continue;

    alert(i);
  }
}

function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if (n % i == 0) return false;
  }

  return true;
}

现在我们可以很容易地理解代码了。函数自己就变成了一个注释。这种代码被称为 自描述型 代码。

配方:创建函数

如果我们有一个像下面这样很长的代码块:

// 在这里我们添加威士忌(译注:国外的一种酒)
for(let i = 0; i < 10; i++) {
  let drop = getWhiskey();
  smell(drop);
  add(drop, glass);
}

// 在这里我们添加果汁
for(let t = 0; t < 3; t++) {
  let tomato = getTomato();
  examine(tomato);
  let juice = press(tomato);
  add(juice, glass);
}

// ...

我们像下面这样,将上面的代码重构为函数,可能会是一个更好的变体:

addWhiskey(glass);
addJuice(glass);

function addWhiskey(container) {
  for(let i = 0; i < 10; i++) {
    let drop = getWhiskey();
    //...
  }
}

function addJuice(container) {
  for(let t = 0; t < 3; t++) {
    let tomato = getTomato();
    //...
  }
}

同样,函数本身就可以告诉我们发生了什么。没有什么地方需要注释。并且分割之后代码的结构也更好了。每一个函数做什么、需要什么和返回什么都非常地清晰。

实际上,我们不能完全避免“解释型”注释。例如在一些复杂的算法中,会有一些出于优化的目的而做的一些巧妙的“调整”。但是通常情况下,我们应该尽可能地保持代码的简单和“自我描述”性。

好的注释

所以,解释性注释通常来说都是不好的。那么哪一种注释才是好的呢?

  • 描述架构

    对组件进行高层次的整体概括,它们如何相互作用、各种情况下的控制流程是什么样的……简而言之 —— 代码的鸟瞰图。有一个专门用于构建代码的高层次架构图,以对代码进行解释的特殊编程语言 UML。绝对值得学习。

  • 记录函数的参数和用法

    有一个专门用于记录函数的语法 JSDoc:用法、参数和返回值。

例如:

/**
 * 返回 x 的 n 次幂的值。
 *
 * @param {number} x 要改变的值。
 * @param {number} n 幂数,必须是一个自然数。
 * @return {number} x 的 n 次幂的值。
 */
function pow(x, n) {
  ...
}

这种注释可以帮助我们理解函数的目的,并且不需要研究其内部的实现代码,就可以直接正确地使用它。

顺便说一句,很多诸如 WebStorm 这样的编辑器,都可以很好地理解和使用这些注释,来提供自动补全和一些自动化代码检查工作。

当然,也有一些像 JSDoc 3 这样的工具,可以通过注释直接生成 HTML 文档。你可以在 http://usejsdoc.org/ 阅读更多关于 JSDoc 的信息。

  • 为什么任务以这种方式解决?

    写了什么代码很重要。但是为什么 那样写可能对于理解正在发生什么更重要。为什么任务是通过这种方式解决的?代码并没有给出答案。如果有很多种方法都可以解决这个问题,为什么偏偏是这一种?尤其当它不是最显而易见的那一种的时候。没有这样的注释的话,就可能会发生下面的情况:你(或者你的同事)打开了前一段时间写的代码,看到它不是最理想的实现方式。你会想:“我当时是有多蠢啊,现在我真是太聪明了”,然后用“更显而易见且正确的”方式重写了一遍。……重写的这股冲动劲是好的。但是在重写的过程中你发现“更显而易见”的解决方案实际上是有缺陷的。你甚至依稀地想起了为什么会这样,因为你很久之前就已经尝试过这样做了。于是你又还原了那个正确的实现,但是时间已经浪费了。解决方案的注释非常的重要。它们可以帮助你以正确的方式继续开发。

  • 代码有哪些巧妙的特性?它们被用在了什么地方?

    如果代码存在任何巧妙和不显而易见的方法,那绝对需要注释。

总结

一个好的开发者的标志之一就是他的注释:它们的存在甚至它们的缺席(译注:在该注释的地方注释,在不需要注释的地方则不注释,甚至写得好的自描述函数本身就是一种注释)。

好的注释可以使我们更好地维护代码,一段时间之后依然可以更高效地回到代码高效开发。

注释这些内容:

  • 整体架构,高层次的观点。
  • 函数的用法。
  • 重要的解决方案,特别是在不是很明显时。

避免注释:

  • 描述“代码如何工作”和“代码做了什么”。
  • 避免在代码已经足够简单或代码有很好的自描述性而不需要注释的情况下,还写些没必要的注释。

注释也被用于一些如 JSDoc3 等文档自动生成工具:它们读取注释然后生成 HTML 文档(或者其他格式的文档)。


忍者代码

学而不思则罔,思而不学则殆。

孔子《论语》

过去的程序员忍者使用这些技巧,来使代码维护者的头脑更加敏锐。

代码审查大师在测试任务中寻找它们。

一些新入门的开发者有时候甚至比忍者程序员能够更好地使用它们。

仔细阅读本文,找出你是谁 —— 一个忍者、一个新手、或者一个代码审查者?

⚠️检测到讽刺意味

许多人试图追随忍者的脚步。只有极少数成功了。

简洁是智慧的灵魂

把代码尽可能写得短。展示出你是多么的聪明啊。

在编程中,多使用一些巧妙的编程语言特性。

例如,看一下这个三元运算符 '?'

// 从一个著名的 JavaScript 库中截取的代码
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

很酷,对吗?如果你这样写了,那些看到这一行代码并尝试去理解 i 的值是什么的开发者们,就会有一个“快活的”的时光了。然后会来找你寻求答案。

告诉他短一点总是更好的。引导他进入忍者之路。

一个字母的变量

道隐无名。夫唯道善贷且成。

老子(道德经)

另一个缩减代码量的方法是,到处使用单字母的变量名。例如 abc

短变量就像森林中真正的忍者一样,一下就找不到了。没有人能够通过编辑器的“搜索”功能找到它。即使有人做到了,他也不能“破译”出变量名 ab 到底是什么意思。

……但是有一个例外情况。一个真正的忍者绝不会在 "for" 循环中使用 i 作为计数器。在任何地方都可以,但是这里不会用。你随便一找,就能找到很多不寻常的字母。例如 xy

使用一个不寻常的变量多酷啊,尤其是在长达 1-2 页(如果可以的话,你可以写得更长)的循环体中使用的时候。如果某人要研究循环内部实现的时候,他就很难很快地找出变量 x 其实是循环计数器啦。

使用缩写

如果团队规则中禁止使用一个字母和模糊的命名 — 那就缩短命名,使用缩写吧。

像这样:

  • listlst
  • userAgentua
  • browserbrsr
  • ……等

只有具有真正良好直觉的人,才能够理解这样的命名。尽可能缩短一切。只有真正有价值的人,才能够维护这种代码的开发。

Soar high,抽象化。

大方无隅,
大器晚成,
大音希声,
大象无形。

老子(道德经)

当选择一个名字时,尽可能尝试使用最抽象的词语。例如 objdatavalueitemelem 等。

  • 一个变量的理想名称是 data 在任何能用的地方都使用它。的确,每个变量都持有 数据(data),对吧?

    ……但是 data 已经用过了怎么办?可以尝试一下 value,它也很普遍。毕竟,一个变量总会有一个 值(value),对吧?

  • 根据变量的类型为变量命名:strnum……

    尝试一下吧。新手可能会诧异 — 这些名字对于忍者来说真的有用吗?事实上,有用的!

    一方面,变量名仍然有着一些含义。它说明了变量内是什么:一个字符串、一个数字或是其他的东西。但是当一个局外人试图理解代码时,他会惊讶地发现实际上没有任何有效信息!最终就无法修改你精心思考过的代码。

    我们可以通过代码调试,很容易地看出值的类型。但是变量名的含义呢?它存了哪一个字符串或数字?

    如果思考的深度不够,是没有办法搞明白的。

  • ……但是如果找不到更多这样的名字呢? 可以加一个数字:data1, item2, elem5……

注意测试

只有一个真正细心的程序员才能理解你的代码。但是怎么检验呢?

方式之一 —— 使用相似的变量名,像 datedata

尽你所能地将它们混合在一起。

想快速阅读这种代码是不可能的。并且如果有一个错别字时……额……我们卡在这儿好长时间了,到饭点了 (⊙v⊙)。

智能同义词

道,可道,非常道。名,可名,非常名。

老子《道德经》

同一个 东西使用 类似 的命名,可以使生活更有趣,并且能够展现你的创造力。

例如,函数前缀。如果一个函数的功能是在屏幕上展示一个消息 — 名称可以以 display… 开头,例如 displayMessage。如果另一个函数展示别的东西,比如一个用户名,名称可以以 show… 开始(例如 showName)。

暗示这些函数之间有微妙的差异,实际上并没有。

与团队中的其他忍者们达成一个协议:如果张三在他的代码中以 display... 来开始一个“显示”函数,那么李四可以用 render..,王二可以使用 paint...。你可以发现代码变得多么地有趣多样呀。

……现在是帽子戏法!

对于有非常重要的差异的两个函数 — 使用相同的前缀。

例如,printPage(page) 函数会使用一个打印机(printer)。printText(text) 函数会将文字显示到屏幕上。让一个不熟悉的读者来思考一下:“名字为 printMessage(message) 的函数会将消息放到哪里呢?打印机还是屏幕上?”。为了让代码真正耀眼,printMessage(message) 应该将消息输出到新窗口中!

重用名字

始制有名,
名亦既有,
夫亦将知止,
知止可以不殆。

老子(道德经)

仅在绝对必要时才添加新变量。

否则,重用已经存在的名字。直接把新值写进变量即可。

在一个函数中,尝试仅使用作为参数传递的变量。

这样就很难确定这个变量的值现在是什么了。也不知道它是从哪里来的。目的是提高阅读代码的人的直觉和记忆力。一个直觉较弱的人必须逐行分析代码,跟踪每个代码分支中的更改。

这个方法的一个进阶方案是,在循环或函数中偷偷地替换掉它的值。

例如:

function ninjaFunction(elem) {
  // 基于变量 elem 进行工作的 20 行代码

  elem = clone(elem);

  // 又 20 行代码,现在使用的是 clone 后的 elem 变量。
}

想要在后半部分中使用 elem 的程序员会感到很诧异……只有在调试期间,检查代码之后,他才会发现他正在使用克隆过的变量!

经常看到这样的代码,即使对经验丰富的忍者来说也是致命的。

下划线的乐趣

在变量名前加上下划线 ___。例如 _name__value。如果只有你知道它们的含义,那就非常棒了。或者,加这些下划线只是为了好玩儿,没有任何含义,那就更棒了!

加下划线可谓是一箭双雕。首先,代码变得更长,可读性更低;并且,你的开发者小伙伴可能会花费很长时间,来弄清楚下划线是什么意思。

聪明的忍者会在代码的一个地方使用下划线,然后在其他地方刻意避免使用它们。这会使代码变得更加脆弱,并提高了代码未来出现错误的可能性。

展示你的爱

向大家展现一下你那丰富的情感!像 superElementmegaFrameniceItem 这样的名字一定会启发读者。

事实上,从一方面来说,看似写了一些东西:super..mega..nice..。但从另一方面来说 — 并没有提供任何细节。阅读代码的人可能需要耗费一到两个小时的带薪工作时间,冥思苦想来寻找一个隐藏的含义。

重叠外部变量

处明者不见暗中一物,
处暗者能见明中区事。

关尹子

对函数内部和外部的变量,使用相同的名称。很简单,不用费劲想新的名称。

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...许多行代码...
  ...
  ... // <-- 某个程序员想要在这里使用 user 变量……
  ...
}

在研究 render 内部代码的程序员可能不会注意到,有一个内部变量 user 屏蔽了外部的 user 变量。

然后他会假设 user 仍然是外部的变量然后使用它,authenticateUser() 的结果……陷阱出来啦!你好呀,调试器……

无处不在的副作用!

有些函数看起来它们不会改变任何东西。例如 isReady()checkPermission()findTags()……它们被假定用于执行计算、查找和返回数据,而不会更改任何它们自身之外的数据。这被称为“无副作用”。

一个非常惊喜的技巧就是,除了主要任务之外,给它们添加一个“有用的”行为。

当你的同事看到被命名为 is..check..find... 的函数改变了某些东西的时候,他脸上肯定是一脸懵逼的表情 — 这会扩大你的理性界限。

另一个惊喜的方式是,返回非标准的结果。

展示你原来的想法!让调用 checkPermission 时的返回值不是 true/false,而是一个包含检查结果的复杂对象。

那些尝试写 if (checkPermission(..)) 的开发者,会很疑惑为什么它不能工作。告诉他们:“去读文档吧”。然后给出这篇文章。

强大的函数!

大道泛兮,
其左可右。

老子(道德经)

不要让函数受限于名字中写的内容。拓宽一些。

例如,函数 validateEmail(email) 可以(除了检查邮件的正确性之外)显示一个错误消息并要求重新输入邮件。

额外的行为在函数名称中不应该很明显。一个真正的忍者会使它们在代码中也不明显。

将多个行为合并到一起,可以保护你的代码不被重用。

想象一下,另一个开发者只想检查邮箱而不想输出任何信息。你的函数 validateEmail(email) 对他而言就不合适啦。所以他不会找你问关于这些函数的任何事而打断你的思考。

总结

上面的所有“建议”都是从真实的代码中提炼而来的……有时候,这些代码是由有经验的开发者写的。也许比你更有经验 ;)

  • 遵从其中的一丢丢,你的代码就会变得充满惊喜。
  • 遵从其中的一大部分,你的代码将真正成为你的代码,没有人会想改变它。
  • 遵从所有,你的代码将成为寻求启发的年轻开发者的宝贵案例。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。