
It so happened that recently I had to read and refactor a lot of terrible javascript code. Working with this code costs a lot of nerves when accompanied, and writing / debugging such code is not pleasant. Thoughts about what makes people write bad code and how to deal with it made me write this article. I do not pretend to any full disclosure of the topic of the struggle for the quality of the code, I want to consider only some aspects that cause the greatest number of problems. As the main tool for optimizing the quality of the code, I suggest using JSLint, which, despite all the advantages, is not a panacea and can only serve as a starting point for further improvement of the code.
All those who have ever had a headache when writing / reading javascript code, please under the cat.
Standard in the service of a person or vice versa?
Javascript discourages. This language is designed to be as simple and unobtrusive as possible so that a person who does not know many of the intricacies can easily begin to write in javascript and solve real problems. The abundance of javascript frameworks, which will be discussed later, only worsens the situation. So, javascript is a dirty language, allowing freedom by the developer. Thus, if you just write as it turns out, then write multiple times as arbitrarily bad - the language does not prohibit. Naturally, for complex projects such an approach is unacceptable.
')
To make the language stricter, there are coding standards. In my practice, I get the most out of it using the code validation tool -
JSLint . If you follow the link, you can read the warning of the author, Douglas Crockford, that JSLint will hurt your feelings. And this is true - I was very hurt when the validator found fifty errors in 50 lines of working code. At first it seemed wild that all these unnecessary conventions should be followed, that you should break your fingers, retrain, put spaces and unnecessary brackets. Write instead
if (element.parentNode && Math.max (offset1, offset2, offset3)> x1) {
arr = new Array ();
for (var i in some_object) {
if (i == qwerty) continue;
arr.push (i);
}
}
something like this:
/ * global qwerty: true, some_object: false * /
if (element.parentNode && Math.max (offset1, offset2, offset3)> x1) {
var arr = [], i;
for (i in some_object) {
if (some_object.hasOwnProperty (i)) {
if (i === qwerty) {
continue;
}
arr.push (i);
}
}
}
Then came across the jQuery library, where all the source code is valid (well, almost), as well as many other good and smart code. I decided to follow this standard as an experiment. As a result, I received a greatly increased quality of the generated code, which is expressed in a smaller number of errors during debugging, better readability, and obviousness of the code. When reading the code, problem areas, design errors, potential vulnerabilities and ambiguities are immediately visible. There is also a useful side effect of using JSLint - in response to a warning from a validator, you sometimes have to think about places that you usually don’t pay attention to usually, as a rule, this leads to correcting the error before its actual manifestation, that is, it makes debugging easier, and sometimes helps to improve the developed application. In general, the programming world would be much cleaner and better if code validation with JSLint would become common practice in development.
Jslint Basic code requirements
I cite further the free interpretation of the text of Douglas Crockford regarding the restrictions imposed on javascript. It is better to read the original, of course.
Global and local variables
Global variables can rightly be called the greatest evil, not only in javascript, but in all programming languages. I think it is not necessary to explain why (if that
is necessary
here ). In javascript, global variables can be declared in a local context, which only aggravates the situation, because sometimes you can declare a global variable by mistake, simply by hurrying, and, if lucky with the variable name, the developer who will accompany your code will go crazy, trying to understand the meaning who tried to put in the global variable author.
Nothing would have happened if JSLint had been used, which prohibits declaring variables without using the var keyword. And if the variable is truly global, JSLint will force you to specify this explicitly, using the comment / * global variablename * /, specifying whether assignment of a variable is allowed in this context.
In strict mode, JSLint teaches us to use var no more than once for each local namespace. This is certainly an inflection, but I would rather take it as a call to refactoring - the variable is best declared immediately before use, which means the ad groups should be scattered around the site, thus localizing the large method local areas as new functions with names that define the essence of the action, thus documenting the code without using the comment, while at the same time improving the validity of JSLint.
Semicolon
A sore question for many programmers, especially Ruby - they don’t like semicolons and that's it. When writing code in javascript, you need to hack on the nose once and for all - semicolons are put after each instruction. Exceptions: for, function, if, switch, try, and while. Please note that you should not put a semicolon when declaring a function:
function f (x) {
}
however, it is necessary to set it when assigning a variable a value:
var f = function (x) {
};
Carry long line
A string of more than 80 characters is bad. Forget about widescreen monitors, it's not about scrolling, the line of code should be covered at a glance. But it is necessary to break the lines especially - so that after the line break, syntactic incompleteness is clearly tracked. JSLint allows line breaks only after the following statements:
, ; : {} ([= <>?! + - * /% ~ ^ | &
==! = <=> = + = - = * = / =% = ^ = | = & = << >> || &&
===! == << = >> = >>> >>> =
JSLint prohibits the translation of a long instruction after a line, a number, an identifier, a closing bracket, or suffix operators:)] ++ -
Local namespaces
In javascript, the local namespace spawns only one instruction - a function. Neither for, nor while, or if do not generate local namespaces. That is, in the following example:
if (x === y) {
var z = 1;
}
alert (z); // one
The variable defined inside the if block is also available outside the block, while
(function () {
var x = 1;
}) ();
alert (x); // error (undefined variable x);
a variable declared in a function will not be accessible outside it, however, if a variable is declared outside a function, and the function is declared in the same context as the variable, the variable will also be visible in the function, in other words:
var x = 1;
function f () {
alert (x);
}
f (); // one
Concerning the understanding of the mechanisms of operation of closures, difficulties usually arise in examples such as this:
for (x = 1; x <10; x + = 1) {
setTimeout (function () {
console.log (x);
}, ten);
}
This example at the first start will produce an output
10, 10, 10, 10, 10, 10, 10, 10, 10, 10
and at the second
19, 10, 10, 10, 10, 10, 10, 10, 10, 10
at the third
28, 10, 10, 10, 10, 10, 10, 10, 10, 10
instead of the expected
1, 2, 3, 4, 5, 6, 7, 8, 9
which can be obtained only with the proper use of the mechanism of visibility of names:
var f = function (x) {
setTimeout (function () {
console.log (x);
}, 0);
};
for (x = 1; x <10; x + = 1) {
f (x);
}
Required Blocks
It is strictly necessary to use a block (drawn by curly brackets) when using if, for, and other instructions.
if (condition) statement;
// should be replaced by
if (condition) {
statement;
}
Both the first and second code work in the same way, but the first one should be avoided in practice.
Language constructs
for in
Such cycles should be used with caution, since passing through all the members of an object or array, this cycle also passes through the inherited properties / methods of the prototype. JSLint requires that the body of such a loop be necessarily wrapped with an if block.
for (name in object) {
if (object.hasOwnProperty (name)) {
....
}
}
switch
Be sure to use break after each case.
void
Do not use it. Better ordinary undefined, in order to avoid misunderstandings.
== and! =
Use with extreme caution. It must be remembered that these operators give the data type, that is, '\ t \ r \ n' == 0 is true. It is better to use === since this operator compares type matching, and do the casting manually using parseInt (str, radix) where the string should be interpreted as a number.
=
It is better not to use the assignment operator as a condition in an if:
if (x = y) {
}
as it usually means
if (x == y) {
}
a reader who reads such a code usually gets confused - maybe there is a mistake here? If you really need to use this construct, you must specify this explicitly using double brackets:
if ((x = y)) {
}
JSLint Summary
Here is not a complete list of JSLint requirements, but only those that most often have to pay attention. I think it will not be difficult for an interested person to go for a complete list at JSLint.com. Here I would also like to add that for many editors there are plugins that allow you to validate code with JSLInt as you type. I personally used plugins for Eclipse and jEdit. There is a plugin for NetBeans. In extreme cases, you can use the on-line validator.