Someday we all wrote (and some write) bad code, and hopefully we all work on improving our skills, and not just reading articles like this.
Although the performance of your product or site is important, it is also important how your code looks. The reason for this is that not only the machine reads your code .
First, sooner or later you will have to re-read your own code, and when that time comes, only well-written code will help you understand what you have written, or figure out how to fix it.
Secondly, if you work in a team or collaborate with other developers, all team members will read your code and try to interpret it as they understand it. To make it easier for them, it is important to follow certain rules when naming variables and functions, limit the length of each line, and preserve the structure of your code.
Finally, let's look at a specific example.
The easiest way to identify bad code, in my opinion, is to try to read the code as if it were a sentence or a phrase .
For example, take a look at this code:
Screenshot of the bad version of the traverseUpUntil function
The above function takes an element and a conditional function and returns the closest parent node that satisfies the conditional function.
const traverseUpUntil = (el, f) => {
Based on the fact that the code should be read as plain text, the first line has three gross deficiencies.
el
can be understood, since such a name is usually used to designate an element, but the name of the parameter f
does not explain anything at all.el.traverseUpUntil(f)
, but this is another problem. let p = el.parentNode
This is the second line. Again the problem with the names, this time with the variable. If someone looked at the code, then, most likely, they would understand what p
. This is the parentNode
the el
parameter. However, what happens when we look at p
used elsewhere, we no longer have a context that explains what it is .
while (p.parentNode && !f(p)) {
In the next line, the main problem we face is a misunderstanding of what it means or does !f(p)
, because f can mean anything . It is assumed that the person reading the code should understand that !f(p)
is checking the current node for satisfaction of a particular condition. If it passes, the cycle is interrupted.
p = p.parentNode
Everything is clear here.
return p
It is not entirely obvious what is returned due to an incorrect variable name.
Screenshot of the good version of the traverseUpUntil function
First we change the parameter names and their order: (el, f) =>
to (condition, node) =>
.
Perhaps you wonder why, instead of “element (Russian element ), I used“ node ”(Russian node ). I used it for the following reasons:
.parentNode
, so why not make it consistent.Then we go to the variable names:
let parent = node
It is very important to fully disclose the value of your variable in its name , so the “p” is now “parent”. You may also have noticed that now we are not starting with getting the parent node node.parentNode
, instead we only get the node.
Go ahead:
do { parent = parent.parentNode } while (parent.parentNode && !condition(parent))
Instead of the usual while
I chose the do ... while
. This means that we need to get the parent node every time before checking the condition, and not vice versa. Using the do ... while
also helps to read the code as plain text.
Let's try to read: "Assign the parent node to the parent to the parent, as long as the parent has the parent node, and the condition function does not return true . " Already much clearer.
return parent
Often, developers prefer to use some kind of common variable ret
(or returnValue
), but this is a pretty bad practice . If you correctly name your returned variables, it becomes obvious what is being returned. However, sometimes the functions can be long and complex, which leads to great confusion. In this case, I would suggest dividing your function into several functions , and if it is still too complex, then perhaps adding comments can help.
Now that you've made the code readable, it's time to remove the unnecessary code . I'm sure some of you have already noticed that we don’t need the parent
variable at all.
const traverseUpUntil = (condition, node) => { do { node = node.parentNode } while (node.parentNode && !condition(node)) return node }
I simply removed the first line and replaced “parent” with “node”. So I skipped the unnecessary step of creating a “parent” and went straight into the loop.
Although “node” is not the best description for this variable, it is satisfactory. But let's not dwell on this, let's rename it. What about "currentNode"?
const traverseUpUntil = (condition, currentNode) => { do { currentNode = currentNode.parentNode } while (currentNode.parentNode && !condition(currentNode)) return currentNode }
That's better! Now, when we read the method, we know that currentNode
always the node we are in now, instead of being “some” node.
Source: https://habr.com/ru/post/421867/