I want to add the ability to review JavaScript files to the Coder module. However, in order to do that we need to agree on what coding standards we should be using for JavaScript.

At present we have the Coding standards for JS files doc which has one or two ideas but isn't complete. We also have the main Coding Standards doc, and while most are transferable to JavaScript, not all are. For example, you can not have a comma at the end of the last array element in JavaScript. Below is a proposal on the coding standards we should adopt for js files. I'd really appreciate a discussion/debate on this and hopefully we can get a consensus on js coding standards in Drupal.

Exceptions to existing coding standards

  1. Arrays should not have a comma at the end of the last array element.
  2. TRUE, FALSE, NULL are not valid in JavaScript and their lower-case equivalents must be used instead.
  3. CamelCase is widely used in js files, e.g. document.getElementById(). While we can still encourage people to not use CamelCase when declaring their own functions and variables, it will be very difficult for Coder to implement a check for this.
  4. Obviously we can't have
    <?php
    // $Id$

    in a JS file, but perhaps we should explicitly say that the header comment block for js files should just be
    // $Id$

  5. String concatenation must be done using + instead of a dot, but the same standards in relation to spacing should apply.

Drupal 6 (and later) Specific Stuff

Drupal 6 saw the introduction of JavaScript themeing and translation of JavaScript files.

  1. There is now a themeing mechanism for JavaScript code. Any modules containing JavaScript which produces HTML content must now provide default theme functions in the Drupal.theme.prototype namespace.
  2. All strings in JavaScript files should be wrapped in Drupal.t() which is an equivalent of the well-known t() function. Likewise, there is also an equivalent to format_plural(), named Drupal.formatPlural(). The parameter order is exactly like their server-side counterparts.

JS specific coding standards

Many of the suggestions below were taken from http://jslint.com/ and I think we should include them as part of the Drupal coding standards.

  1. JavaScript code should not be embedded in the HTML where possible, as it adds significantly to pageweight with no opportunity for mitigation by caching and compression.
  2. All variables should be declared with var before they are used and should only be declared once. Doing this makes the program easier to read and makes it easier to detect undeclared variables that may become implied globals.
  3. JavaScript does not have block scope - only function scope. This can confuse programmers familiar with other C-family languages. To avoid this, all variables should be declared at the beginning of a function.
  4. All functions should be declared before they are used. Inner functions should follow the var statement so as to make it clear what variables are included in its scope.
  5. If a function literal is anonymous, there should be one space between the word function and the left parenthesis. If the space is omitted, then it can appear that the function's name is function.
    div.onclick = function (e) {
      return false;
    };
    
  6. JavaScript allows any expression to be used as a statement and uses semi-colons to mark the end of a statement. However, it attempts to make this optional with "semi-colon insertion", which can mask some errors. All statements should be followed by ; except for the following:for, function, if, switch, try, while
    The exception to this are with functions declared like
    Drupal.behaviors.tableSelect = function (context) {
      statements;
    };

    and

    do {
      statements;
    } while (condition);
    

    These should all be followed by a semi-colon.

  7. The try class of statements should have the following form:
    try {
      statements;
    }
    catch (variable) {
      statements;
    }
    finally {
      statements;
    } 
    
  8. As a further defense against the semicolon insertion mechanism, long statements should only be broken after one of these punctuation characters or operators:
    , . ; : { } ( [ = < > ? ! + - * / % ~ ^ | &
    A long statement should not be broken after an identifier, a string, a number, closer, or a suffix operator: ) ] ++ --
  9. The for in statement allows for looping through the names of all of the properties of an object. Unfortunately, all of the members which were inherited through the prototype chain will also be included in the loop. This has the disadvantage of serving up method functions when the interest is in data members. To prevent this, the body of every for in statement should be wrapped in an if statement that does filtering. It can select for a particular type or range of values, or it can exclude functions, or it can exclude properties from the prototype. For example:
    for (variable in object) if (filter) {
      statements;
    }

    You can use the hasOwnProperty method to distinguish the true members of the object:

    for (variable in object) if (object.hasOwnProperty(variable)) {
      statements;
    }
  10. The with statement was intended to provide a shorthand in accessing members in deeply nested objects. So instead of using:
    foo.bar.foobar.abc = true;
    foo.bar.foobar.xyz = true;
    

    You can use the shorthand:

    with (foo.bar.foobar) {
      abc = true;
      xyz = true;
    }
    

    It's impossible to know by looking at the code which abc and xyz will get modified. Does foo.bar.foobar get modified? Or do the global variables abc and xyz? Fortuanately there is a better alternative which uses var instead:

    var o = foo.bar.foobar;
    o.abc = true;
    o.xyz = true;
    

    So never use the with statement, use a var instead.

  11. The == and != operators do type coercion before comparing. This is bad because it causes
    ' \t\r\n' == 0
    

    to be true. This can mask type errors. When comparing to any of the following values, use the === or !== operators, which do not do type coercion:0 '' undefined null false true

  12. Avoid the use of the comma operator except for in the control part of for statements. This does not apply to the comma separator (used in object literals, array literals, etc.)
  13. Don't follow a + with + or ++. This pattern can be confusing and the + + can be misread as ++. Use parentheses to make your intention clear.
    Wrong:
        total = subtotal + +myInput.value;
    

    Correct:

        total = subtotal + (+myInput.value);
    
  14. To prevent unreachable code, a return, break, continue, or throw statement should be followed by a } or case or default.
  15. A return statement with a value should not use parentheses around the value. The return value expression must start on the same line as the return keyword in order to avoid semi-colon insertion.
  16. Constructors are functions that are designed to be used with the new prefix. The new prefix creates a new object based on the function's prototype, and binds that object to the function's implied this parameter. JavaScript doesn't issue compile-time warning or run-time warnings if a required new is omitted. If you neglect to use the new prefix, no new object will be made and this will be bound to the global object (bad). Constructor functions should be given names with an initial uppercase and a function with an initial uppercase name should not be called unless it has the new prefix.
  17. Use literal expressions instead of the new operator:
    • Instead of new Array() use []
    • Instead of new Object() use {}
    • Don't use the wrapper forms new Number, new String, new Boolean.
  18. eval is evil. It effectively requires the browser to create an entirely new scripting environment (just like creating a new web page), import all variables from the current scope, execute the script, collect the garbage, and export the variables back into the original environment. Additionally, the code cannot be cached for optimisation purposes. It is probably the most powerful and most misused method in JavaScript. It also has aliases. So do not use the Function constructor and do not pass strings to setTimeout() or setInterval() .

Comments

katbailey’s picture

+1 to nailing down js coding standards. Should we maybe also include the new D6 behaviours standard or is that too jQuery-specific? Coder could check to see if the code uses $(document).ready(function(){...}); and suggest wrapping the code in Drupal.behaviors.myModuleFunction = function (context) {...}; instead.

catch’s picture

Just to say this is likely to get more attention as a documentation issue than in the forums: http://drupal.org/project/issues/documentation

sun’s picture

+1

Re: JS specific coding standards

#2 + #3 could be merged into one point and could benefit from a clear statement like "Variables should not be defined in the global scope; try to define them in a local (function) scope at all costs."

#4: Isn't that superfluous? Can I use a function that has not been declared yet?

#8: I don't really understand this point. Speaking of usual jQuery code, I think we tend to break lines after jQuery methods and indent the next line accordingly? For example:

  $(this).do(foo).what(ever, null)
    .find('#me').bar('baz:not(.processed)');

#9: Following Drupal's PHP standards, the filtering if (filter) {... should be placed on its own line, indented accordingly.

#12-#15: I completely did not understand those points. Are there examples? The example for #13 looks, erm, weird?

...I completely agree with all other points. Actually, I have learned a lot from this initial draft.

However, we want to make sure to add the Use $ prefix for jQuery variables standard, because that one makes working with jQuery a lot cleaner.

btw: Stella initiated a Wiki page for JS coding standards over at groups.drupal.org (but we can't discuss there).

Daniel F. Kudwien
unleashed mind

Daniel 'sun' Kudwien
makers99

quicksketch’s picture

This looks really fantastic. I don't think there are any actual errors in this proposal, and it fits with our practices in core (even though core itself is incosistent, this matches most of the core usage).

Like sun says, some more examples and clarifications are necessary. I'm specifically missing a reason for #13 and especially #17.

I agree with #10, but it takes a while to get to the ultimate conclusion that you shouldn't use the with type declaration. Perhaps instead of "You can use the shorthand:", it could be, "While possible to use the shorthand:... You should avoid this syntax because...".

You should always refer to camelCase with a lowercase first letter, not "CamelCase", which implies incorrect usage of an uppercase first letter.

Lastly, let's use "theming" instead of "themeing". While theming is not technically a real word, it should drop the "e" just the same as "scheme" becomes "scheming".

Nathan Haug
creative graphic design        w: quicksketch.org
& software development       e: nate@quicksketch.org

pwolanin’s picture

Overall this is good, but there seem to be enough exceptions and caveats that I would suggest making a full, parallel JS coding style document. I don't think the PHP one is all that long.

I found this bit confusing, though maybe my lack of JS - when you assign to a variable from an existing object, it's a reference rather than a copy?

You can use the shorthand:

with (foo.bar.foobar) {
  abc = true;
  xyz = true;
}

It's impossible to know by looking at the code which abc and xyz will get modified. Does foo.bar.foobar get modified? Or do the global variables abc and xyz? Fortuanately there is a better alternative which uses var instead:

var o = foo.bar.foobar;
o.abc = true;
o.xyz = true;

So never use the with statement, use a var instead.

---
Work: BioRAFT

stella’s picture

If you assign a string or boolean to a variable in JS, then it creates a copy. However, when assigning an array or object to a variable, it makes a reference to the value. Confusing, I know. There's no easy way either to tell JS to do one instead of the other, like there is in PHP with the =& operator. There are ways around this, but it's probably too complicated to get into here. I'll be posting a blog article on this later this week if anyone is interested.

stella’s picture

I've updated the JS coding standards doc at http://drupal.org/node/172169 in the handbook with the above proposal and incorporating as many as your suggestions as I could. I've removed a couple of the points which didn't make much sense (other than for readability perhaps). There might be a need for some more examples, but at least in the handbook I can go back and make changes without having to repost the entire list. Feel free to adjust things that aren't clear to you. We can continue the discussion here if we need to.

Thanks again for all your suggestions and feedback.

Cheers,
Stella