Improving the quality of javascript code. Jsint


    It so happened that recently I had to read and refactor a lot of terrible javascript code. Working with such a code costs a lot of nerves when accompanied, and writing / debugging such a code is not nice. Thoughts on what makes people write bad code and how to deal with it made me write this article. I do not pretend to reveal the topic of the struggle for the quality of the code in any way, I want to consider only some aspects that cause the greatest number of problems. I propose using JSLint as the main tool for optimizing the quality of the code, which, despite all the advantages, is not a panacea and can only serve as a starting point for further improvement of the code.

    I ask everyone who at least once had a headache when writing / reading javascript code, I ask for cat.

    A standard in the service of man or vice versa?



    JavaScript cools. This language is designed to be as simple and unobtrusive as possible, so that a person who does not know many tricks can easily start writing 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 that allows liberties on the part of the developer. Thus, if you just write how it turns out, then writing a lot is arbitrarily bad - the language does not prohibit. Naturally, for complex projects this approach is unacceptable.

    To make the language simpler, 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 had to break your fingers, retrain your fingers, 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 the jQuery library caught my eye, where all the source code is valid (well, almost), as well as a lot of 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 fewer errors during debugging, better readability, and obviousness of the code. When reading the code, the problem areas, design errors, potential vulnerabilities and ambiguities become immediately visible. There is also a useful side effect of using JSLint - in response to a validator warning, you sometimes have to think in places that you simply don’t pay attention to usually, as a rule this leads to fixing the error before it actually appears, that is, it facilitates debugging, and sometimes it helps to improve the architecture application under development. Generally,

    JSLint. Basic code requirements



    Further, I give a free interpretation of the text of Douglas Crockford regarding the restrictions imposed on javascript. Better to read, the original, of course.

    Global and local variables



    Global variables can rightfully be called the greatest evil not only in javascript, but also in all programming languages. I think there is no need to explain why (if necessary, here it is ). In javascript, global variables can be declared in a local context, which only exacerbates the situation, because sometimes you can declare a global variable by mistake, just hurry up, and if you are lucky with the variable name, the developer who will accompany your code will go crazy trying to understand the meaning , which the author tried to embed in a global variable.

    None of this would have happened if JSLint was used, which prohibits declaring variables without using the var keyword. And if the variable is truly global, JSLint will force you to indicate this explicitly, using the comment / * global variablename * /, specifying whether the assignment of a variable to a value is allowed in this context.

    In strict mode, JSLint teaches us to use var no more than once for each local namespace. This is of course an inflection, but I would better take it as a call for refactoring - after all, it is best to declare a variable immediately before use, which means that ad groups should be scattered by the method, localizing the places of use, so it would be better to format the local areas of the big method as new functions with names defining the essence of the action, thus documenting the code, not using a comment, while improving JSLint validity.

    Semicolon



    A sore point for many programmers, especially Ruby - they do not like to put semicolons and that's it. When writing code in javascript, you need to chop it on the nose once and for all - semicolons are placed after each instruction. Exceptions: for, function, if, switch, try, and while. Note that you do not need to put a semicolon when declaring a function:
    function f (x) {
    }

    however, you must set it when assigning a value to a variable:
    var f = function (x) {
    };


    Wrap Long Line



    A string of more than 80 characters is bad. Forget about widescreen monitors, it's not about scrolling, a line of code should be covered at a glance. But you also need to break lines separately - so that at the place of line breaks, syntactic incompleteness is clearly tracked. JSLint allows line breaks only after the following statements:
    ,. ; : {} ([= <>?! + - * /% ~ ^ | &
    ==! = <=> = + = - = * = / =% = ^ = | = & = << >> || &&
    ===! == << = >> = >>> >>> =

    JSLint prohibits the translation of a long instruction after a string, number, identifier, closing parenthesis or suffix operators:)] ++ -

    Local namespaces



    In javascript, the local namespace only generates one instruction - a function. Neither for, nor while, nor if generate local namespaces. That is, in the following example:
    if (x === y) {
        var z = 1;
    }
    alert (z); // 1

    The variable defined inside the if block is also available outside the block, while
    (function () {
        var x = 1;
    }) ();
    alert (x); // error (undefined variable x);

    the variable declared in the function will not be accessible outside it, however, if the variable is declared outside the 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 (); // 1


    Regarding 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);
        }, 10);
    }

    This example at the first start will generate a conclusion of
    10, 10, 10, 10, 10, 10, 10, 10, 10, 10
    and with 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 only be obtained using the correct use of the name visibility mechanism:
    var f = function (x) {
        setTimeout (function () {
            console.log (x);
        }, 0);
    };
    for (x = 1; x <10; x + = 1) {
        f (x);
    }


    Mandatory Blocks



    It is strictly necessary to use a block (drawn with curly braces) when using if, for, and other instructions.

    if (condition) statement;
    // must be replaced by
    if (condition) {
    	statement;
    }

    Both the first and second code work the same, but the first should be avoided in practice.

    Language constructs


    for in


    This kind of cycles should be used with caution, since passing through all the members of an object or array, this cycle will also pass through the inherited properties / methods of the prototype. JSLint requires that the body of such a loop be necessarily wrapped in an if block.
    for (name in object) {
        if (object.hasOwnProperty (name)) {
            ....
        }
    }

    switch


    Be sure to use break after each case

    void


    No need to use this. Better than usual undefined, in order to avoid misunderstanding.

    == and! =


    Use with extreme caution. It must be remembered that these operators cast a data type, i.e. '\ t \ r \ n' == 0 is true. It’s better to use === because this operator compares type matching, and cast manually using parseInt (str, radix) where you need to interpret the string as a number.

    =


    It is better not to use the assignment operator as a condition in if:
    if (x = y) {
    }

    because usually it means
    if (x == y) {
    }

    those reading this code usually have a misunderstanding - maybe there is a mistake here? If you really need to use such a construction, you must specify this explicitly using double brackets:
    if ((x = y)) {
    }


    JSLint Summary



    This is not a complete list of JSLint requirements, but only those that you have to pay most attention to. I think it will not be difficult for an interested person to go to JSLint.com for a complete list. Here I would also like to add that for many editors there are plugins that allow you to validate the code using 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.

    Also popular now: