
有时候我们都写(有些写)不好的代码,我希望我们所有人都在努力提高自己的技能,而不仅仅是读这样的文章。
为什么我们需要编写好的代码,而不仅仅是生产性代码?
尽管产品或站点的性能很重要,但是代码的外观也很重要。 这样做的原因是不仅机器正在读取您的代码 。
首先,迟早您将不得不重新阅读自己的代码,而到那时,只有编写良好的代码才能帮助您理解所编写的内容或弄清楚如何对其进行修复。
其次,如果您在团队中工作或与其他开发人员合作,那么团队中的所有成员将阅读您的代码并尝试以他们理解的方式对其进行解释。 为使它们更容易使用,在命名变量和函数时,请遵循某些规则,限制每行的长度,并保留代码的结构,这一点很重要。
最后,让我们看一个具体的例子。
第1部分:如何识别错误代码?
我认为,识别错误代码的最简单方法是尝试将代码视为句子或短语来读取 。
例如,看下面的代码:

traverseUpUntil的错误版本的屏幕截图
上面提供的函数接受一个元素和一个条件函数,并返回满足条件函数的最接近的父节点。
const traverseUpUntil = (el, f) => {
基于应该像纯文本一样阅读代码的事实,第一行存在三个重大缺陷。
- 函数参数读起来不像单词 。
- 可以理解
el
,因为这样的名称通常用于表示元素,但是参数f
的名称根本无法解释任何内容。 - 该函数的名称可以这样读取:“切换直到el通过f”,最好将其读作“切换直到el通过f”。 当然,执行此操作的最佳方法是允许将函数调用为
el.traverseUpUntil(f)
,但这是另一个问题。
let p = el.parentNode
这是第二行。 再次是名称的问题,这次是变量。 如果有人看了代码,那么很可能他们会理解p
什么。 这是el
参数的parentNode
。 但是,当我们查看在其他地方使用的p
时会发生什么, 我们不再有解释它是什么的上下文 。
while (p.parentNode && !f(p)) {
在下一行中,我们面临的主要问题是对!f(p)
含义或作用的认识不足,因为“ f”可能意味着任何事情 。 假定阅读代码的人应该理解, !f(p)
是当前节点满足特定条件的检验。 如果通过,则循环被中断。
p = p.parentNode
这里的一切都清楚了。
return p
由于变量名无效,返回的结果并不完全清楚。
第2部分:重构

traverseUpUntil的良好版本的屏幕截图
首先,我们更改参数的名称及其顺序: (el, f) =>
更改为(condition, node) =>
。
您可能想知道为什么我使用“ node”(俄罗斯节点 )代替“ element(俄罗斯元素 )”。 我使用它的原因如下:
- 我们根据节点(例如
.parentNode
编写代码,所以为什么不使其保持一致。 - “节点”比“元素”短,并且含义不丢失 。
然后我们转到变量名:
let parent = node
在名称中完全公开变量的值非常重要,因此“ p”现在是“ parent”(俄罗斯父母 )。 您可能还注意到,现在我们不首先获取父节点node.parentNode
,而是仅获取节点。
我们走得更远:
do { parent = parent.parentNode } while (parent.parentNode && !condition(parent))
我选择了do ... while
while
不是通常的do ... while
。 这意味着我们需要在检查条件之前每次都获取父节点,反之亦然。 使用do ... while
还有助于读取诸如纯文本的代码。
让我们尝试阅读: “只要父级具有父级节点,并且条件函数不返回true,就将父级的父级节点分配给父级 。 ” 已经更加清晰了。
return parent
开发人员通常更喜欢使用某种公共变量ret
(或returnValue
), 但这是非常糟糕的做法 。 如果您正确命名返回变量,则很明显返回了什么。 但是,有时功能可能会很长且很复杂,从而导致很多混乱。 在这种情况下, 我建议将您的函数分为几个函数 ,如果仍然太复杂,也许添加注释会有所帮助。
第3部分:代码简化
既然您已经使代码可读,那么该删除不必要的代码了 。 我敢肯定,有些人已经注意到我们根本不需要parent
变量。
const traverseUpUntil = (condition, node) => { do { node = node.parentNode } while (node.parentNode && !condition(node)) return node }
我只是删除了第一行,并用“节点”替换了“父”。 因此,我跳过了不必要的创建“父母”步骤,直接进入了循环。
但是变量名呢?
尽管“节点”不是对该变量的最佳描述,但它是令人满意的。 但是,我们不止于此,我们对其进行重命名。 那“ currentNode”呢?
const traverseUpUntil = (condition, currentNode) => { do { currentNode = currentNode.parentNode } while (currentNode.parentNode && !condition(currentNode)) return currentNode }
那更好! 现在,当我们阅读该方法时,我们知道currentNode
始终代表着我们现在所在的节点,而不是“某种”节点。