每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;微博:https://weibo.com/openingsource;电报群 https://t.me/OpeningSourceOrg


今日推荐开源项目:《猴子也能看懂的 TensorFlow 教程 Easy-TensorFlow》GitHub链接

推荐理由:虽然说猴子大概是看不懂这个的,但是这个项目依然是一个很全面的教程,从安装到如何创建表到各种各样的类型再到重头戏如何训练神经网络,相信对于初学者来说已经可以解决不少问题了,强烈推荐给学习 TensorFlow 的朋友们,有了这个教程可以少走不少弯路。


今日推荐英文原文:《How To Make Your Code Readable》作者:Christopher Brown

原文链接:https://medium.com/@chbchb55/the-importance-of-readable-code-165895e939c7

推荐理由:“’当我写这段代码的时候,只有神仙和我知道我在写什么……好了,现在只有神仙知道了”

How To Make Your Code Readable

We’ve all seen (and written) bad code at one point or another and hopefully we’re all working at bettering these skills and not just learning the newest framework out there.

Why do we need to write good code, not just performant code?

While the performance of your product or site is important, so is the way your code looks. This reasoning behind this is that the machine isn’t the only entity reading your code.
First, and foremost, you are eventually going to have to re-read some portion of your code, if not the whole thing, and when that time comes performant code isn’t going to help you understand what you’ve written or figure out how to fix it.
Second, if you work on a team or collaborate with other developers then any time you write code your team members have to read your code and try to interpret it in a way they understand. To make that easier for them, it’s important to consider how you name variables and functions, the length of each line, and the structure of your code, among other things.
Lastly, it’s just nicer to look at.

Part 1: How do you identify bad code?

The simplest way to identify bad code, in my opinion, is to try to read your code as if it were a sentence or phrase.

For example, here is some code:

The pictured function above, when passed an element and a conditional function returns the nearest parent node that passes the conditional function.

const traverseUpUntil = (el, f) => {

Following the idea that code should be readable like regular writing, the first line has three fatal flaws.

  • The parameters for the function are not readable like words.
  • While el can be understood as it is commonly used to mean element, the parameter name f does not explain its purpose.
  • If you were to use the function it would read like so: “traverse up until el passes f” which could probably better read as “traverse up until f passes, from el”. Granted, the best way to actually do this would to allow the function to be called like el.traverseUpUntil(f) but that’s a different problem.
let p = el.parentNode

This is the second line. Again we have a naming issue, this time with a variable. If one were to look at the code they would most likely understand what p is. It is the parentNode of parameter el . However, what happens when we look at p used anywhere else, we no longer have the context that explains what it is.

while (p.parentNode && !f(p)) {

In this line, the main problem we encounter is not knowing what !f(p) means or does, because “f” could mean anything at this point. What the person reading the code is supposed to understand is that !f(p) is a check to see if the current node passes the condition. If it does, stop the loop.

p = p.parentNode

This one is pretty self-explanatory.

return p

Not 100% clear what is being returned due to the bad variable name.

Part 2: Let’s make improvments

First we modify the parameter names and their order:(el, f) => into (condition, node) => (you can also do condition => node => which adds an extra layer of useability)
You might be wondering why, instead of using “element”, I used “node”. I used it because of the following:

  • We are already writing code in terms of nodes, for example .parentNode , so why not make it all the same.
  • It’s shorter than writing element without losing it’s meaning. And the reason why I say this is that it works with all forms of nodes that have the property “parentNode”, not just HTML elements.

Next we touch up on the variable name(s):

let parent = node

It’s very important to fully elaborate the meaning of your variable within its name, “p” is now “parent”. You may have also noticed we aren’t starting out by getting node.parentNode , instead we only get node .

This leads us into our next few lines:

do {
  parent = parent.parentNode
} while (parent.parentNode && !condition(parent))

Instead of a regular while loop I’ve opted for a do … while loop. This means that we only have to get the parent node once, as it runs the condition after the action, not the other way around. The use of the do … while loop also reaches back to being able to read the code like writing.

Let’s try reading it: “Do parent equals parent’s parent node while there is a parent node and the condition function doesn’t return true.” While that may seem a bit weird it helps us understand what the code means when we can easily read it.

return parent

While many people opt to use the generic ret variable (or returnValue), it is not a good practice to name the variable you return “ret”. If you name your return variables appropriately it becomes obvious to what is being returned. However, sometimes functions can be long and daunting causing it to be more confusing. In this instance, I would suggest splitting your function into multiple functions and if it’s still too complicated adding comments can help.

Part 3: Simplify the code

Now that you’ve made the code readable it’s time to take out any unnecessary code. As I’m sure some of you have already noticed, we probably don’t need the variable parent at all.

const traverseUpUntil = (condition, node) => {
  do {
    node = node.parentNode
  } while (node.parentNode && !condition(node))
  
  return node
}

What I’ve done is taken out the first line and replaced “parent” with “node”. This bypasses the unnecessary step of creating “parent” and goes straight to the loop.

But what about the variable name?

While “node” isn’t the best descriptor for this variable, it’s a decent one. But let’s not settle for decent, let’s rename it. How about “currentNode”?

const traverseUpUntil = (condition, currentNode) => {
  do {
    currentNode = currentNode.parentNode
  } while (currentNode.parentNode && !condition(currentNode))
  
  return currentNode
}

That’s better! Now when we read it we know that no matter what currentNode will always represent the node we are currently at instead of it just being some node.


每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;微博:https://weibo.com/openingsource;电报群 https://t.me/OpeningSourceOrg