📜 ⬆️ ⬇️

Static JavaScript analyzers and errors from which they will help to unlearn (Part 2)

We continue to translate the article on static analyzers: in the last part, the author touched upon such nuances as the use of == and === operators, as well as undefined variables and late definitions, and the author points out the comments that the analyzers give (using JSHint as an example). detection of similar errors. In this part we will discuss the re-declaration of the variable, as well as control over the cyclomatic complexity of the code.


Redeclare (use) a variable
In JavaScript, you can redeclare (use) variables, but this is almost always random. See:

function incrementCount(counter) { if (counter.count) { counter.count++; } else { var counter = 1; counter.count = counter; } } 

')
In this function, we increment the count property for the presented object, but we need to add a property if it is not there yet. See the bug?

This feature will never add or increment a counter. The else expression will always be called, and it will redefine the argument of the counter function. Basically, this function creates a new object, assigns a property to it, and then loses an object when the function returns. It never changes the object that was presented.

This simple type will allow you to run the code without error, but will lead to a rather strange result.

JSHint will show the following:

 test.js: line 21, col 21, 'counter' is already defined. 


Curly brackets in blocks, cycles and conditional constructions

 if (false) doSomethingElse(); doSomething(); 


What will do this code - doSomething or doSomethingElse? At first glance, it always seems to me that he will not do doSomething or doSomething Else. This is how it works in Python, but not in JavaScript. JavaScript, in the first place, will execute the string, which is located under the if expression, as part of a block. Indent does not matter.

The whole problem lies in the readability of the code. If you do not understand what the code will do, you will write bugs.
Python and CoffeeScript love to skip curly braces. This works well in languages ​​that use free space formatting, but JavaScript behaves differently. JavaScript allows you to create a large number of strange syntax, while braces help to avoid trouble.

 if (false) { doSomethingElse(); doSomething(); } 


Add parentheses, and your code will always be more readable. Skip them and JSHint will show you the following:

 test.js: line 27, col 5, Expected '{' and instead saw 'doSomething'. 


Single and double quotes

 console.log("This is a string. It's OK."); console.log('This string is OK too.'); console.log("This string " + 'is legal, but' + "really not OK."); 


JavaScript allows you to define strings with single and double quotes. This is good when you have flexibility, such as in defining HTML, but additional flexibility can lead to the creation of very inconsistent code.

Google has a code style guide that always uses single quotes for strings, so they don’t need to remove double quotes in HTML. I can't argue that single quotes are better than double quotes, but I can bet that consistency is important here. Maintaining consistency makes the code more readable.

JSHint will warn you about mixing quotes like this:

 test.js: line 31, col 27, Mixed double and single quotes. 


Copy and paste or incorrectly write quotes is quite simple. As soon as you put the wrong quotes, the rest will start doing that too, especially if the file is edited by several people. Static analyzers preserve matching quotes and help avoid a lot of cleaning in the future.

Cyclomatic complexity

Cyclomatic complexity is a measure of the complexity of a given code block. Look at the code and count the number of branches on which it can work - this number is the cyclomatic complexity.
For example, the cyclomatic complexity of this code is 1:

 function main() { return 'Hello, World!'; } 


You can trace only one branch of the execution of this code.
Let's add conditional logic:

 function main() { if (true) { return 'Hello, World!'; } else { return 'Hello, unWorld!'; } } 


Cyclomatic complexity increased to 2.

Perfect code is easy to read and understand. The higher the cyclomatic complexity, the more difficult it will be to understand the code. Everyone agrees that high cyclomatic complexity is bad, but no one can come to a certain limit; 5 is good, and 100 is too much. And in the middle there is too much uncertainty.

If the cyclomatic complexity reaches a predefined limit, JSHint will inform you about it.

 test.js: line 35, col 24, This function's cyclomatic complexity is too high. (17) 


JSHint is the only one of three testing programs that takes into account cyclomatic complexity. It also allows you to set a limit. Exceed the number of maxcomplexity you specified, and JSHint will warn you. I like to set a limit of 14, but I can make it a little higher in projects where I need a lot of parsing.

In fact, the complexity value is important because it tells you when to reorganize the code. The first time you write a long function, it matters. But when you wait half a year and then return to the code to fix the bugs, you'll be glad you took the time to make it more readable.

Cyclomatic complexity is usually broken down into a list. For example, I created a calendar, and I wanted to set the correct first day of the week for each country. I had a function that looked like this:

 function getFirstDay(country) { if (country === 'USA') { return 'Sunday'; } else if (country === 'France') { return 'Monday'; } else if… } 


I needed to cover many countries, so the cyclomatic complexity quickly jumped over the 50 mark. Despite the fact that the code was quite readable, the number of branches was huge, so my code analyzer was unhappy. In the end, I separated the function and got the difficulty below my maximum. It was not an easy task for this particular case, but it is a low price for a cleaner code as a whole.

Check everything you edit more than once.

Static analyzers find bugs that you would not have discovered with simple testing. They also find bugs in the process of compiling, and not during the work - these are the very midnight bugs that creep in when a dozen people take on one thing. Finding all these unobtrusive bugs without checking the code is a long and painful process.

I started this article with the statement that I always use a code analyzer, but I do not do it in one case: when I write a one-time code. I like to use fast prototypes to demonstrate interactive ideas and help my team understand how things should work. Such prototypes are one-time codes; I don’t need to fix bugs here, because I’ll throw this code in a few weeks. Such a one-time code exists solely for brief demonstrations, and it does not matter to me whether it contains unobtrusive bugs. And everything that is important to me is being analyzed.

Fixing these types of bugs at the beginning of the project is pretty easy. Finding them the night before the release can drive you crazy. Code analyzers have saved my life many times, and therefore yours will be saved.

Source: https://habr.com/ru/post/251329/


All Articles