The following are coding conventions for the JavaScript programming language. They are not meant to be restrictive; rather, they are based upon best practices that have been developed over the years within the JavaScript community. The following are based upon suggestions by Douglas Crockford and Nicholas Zakas. Both are front-end engineers for Yahoo!. Crockford is the discoverer of JSON as well as other widely-distributed and useful tools such as JSLint and a published author (O'Reilly). Zakas is a contributor to the YUI JavaScript library, having written the Cookie Utility, Profiler, and YUI Test packages and is also a published author (Wrox).
Strings should be double-quoted.
All JavaScript should be housed in its own location and linked to externally. When embedded within (X)HTML, JavaScript has no chance to be either cached or compressed and maintainability increases as scalability decreases. For example, HTML events, i.e. inline JavaScript that binds a handler to an event (onload, onclick, onchange, etc.), should be scrupulously avoided as this can be especially time-consuming to maintain and/or to transition to a library or to the W3C recommended event listener pattern based upon the publish-subscribe design pattern. Scripts should always have the .js file extension. When linking to an external resource, use the following HTML:
<script type="text/javascript" src="filename.js"></script>
The type attribute should always go before the src attribute. The charset attribute is not needed unless character encoding employed by the external JavaScript file differs from that of the main document. If used, it should come between the type and src attributes. The language attribute is deprecated and should not be used. Incidentally, though needed for page validation, the type attribute is not technically needed as the MIME type is always determined by the server.
Use two spaces. Since JavaScript tends to be nested more than traditional OO languages, using more than two spaces for indentation can quickly lead to code that is indented off the page and thus harder to read. Do not use tabs.
Comments in JavaScript should be used copiously, especially when a tricky or non-intuitive solution is used. Only use // for single-line quotes, otherwise use /* ... */ for multi-line quotes (as when documenting or commenting out large blocks of text).
All variables should be declared using the var keyword, otherwise they become global and are properties of the global window object. Neglecting to precede a variable declaration with the var keyword will make the variable global even when nested within a function. All variables, including functions, should be declared at the beginning of the function body. When declaring multiple variables, do so using only one statement. This is done for optimization reasons. When declaring one or two variables, do so on the same line. For more than two variable declarations, each variable should be placed on its own line and indented two spaces to enhance readibility:
var oDiv, sHtml; //one or two variable declarations; var oDiv, //more than two variable declarations; sHtml, aPeople = ["Bill", "Tom", "Alice"];
In addition, variable names should never include anything but alpha-numeric characters. Variables preceded by an underscore (_) are sometimes interpreted as meaning that it should be treated as a private variable, but only true private variables can be created via the module pattern and thus the underscore should not be used.
It is ok to declare and define a variable in one statement.
Since functions are first-class objects in JavaScript, they too can be declared using the var keyword, although this is functionally equivalent to declaring functions in the traditional manner. Please review the following function constructs and the proper spacing for each:
var fnFunctionName = function () { //var[space]fnFunctionName[space]=[space]function[space]()[space]{ ... }; function fnFunctionName() { //function[space]fnFunctionName()[space]{ ... } //these are virtually equivalent;
Please note that the opening bracket is always on the same line as the function name. Note also the semi-colon after the first function declaration; this is proper as it's a variable definition. In addition, function parameters or arguments should only have a space after the comma (i.e., in a method signature with multiple parameters) and no spaces at all for a single parameter. For example:
var fnAverage = function (iHigh, iMiddle, iLow) { ... }; //multiple arguments; var fnOne = function (oMyObject) { ... }; //one argument;
It is recommended to use an object literal in the method signature. This makes moot the order of the variables, enhances readability and easily allows for default values.
Lastly, it's recommended to declare functions as variables using the var keyword as this helps to enforce the notion that functions are simply variables in JavaScript and are treated thusly.
Anonymous functions, sometimes referred to as lambda functions, are prevalent in JavaScript and are extremely useful as function arguments such as callbacks. Further, if a function is a one-off, it's best to use an anonymous function instead of littering the scope chain with another variable name. Another extremely important use of anonymous functions is that of creating block-level scope, which formally doesn't exist in JavaScript. Remember that JavaScript only formally support function-level scope. Here are some examples of common uses of an anonymous function:
//use an anonymous function or a named function variable withsetTimeoutandsetIntervalrather than a string (which is evaluated and inefficient); setTimeout(function () { ... }, 500); /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ /** * this anonymous function is defined and immediately executed creating a block-level scope that is inaccessible from the outside; * note the added parenthesis wrapping the anonymous function; * note that arguments can be passed when the anonymous function is executed; */ (function ($) { $(function () { ...code executed on page load... }); })(jQuery); /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ //module pattern; var oMyModule = (function () { //private vars (b/c of closure); var bReturn = true, fnHello = function (sMessage) { alert("Hello, " + sMessage); }; //return public API; return { init: function (sMessage) { fnHello(sMessage); }, isTrue: function () { return bReturn; } }; })(); oMyModule.init("world!");
Since JavaScript is a weakly-typed language, the proper naming of variables is a key way to improving readibility. Verbose names are always better than non-sensical short names. Use a subset of Hungarian notation when naming variables. For example,
Use vVariableName (variant) as a name when the data type could change over the course of the variable's lifetime or when, as a function argument, it could expect different types of data as input. As mentioned earlier, names should only contain alpha-numeric characters, always beginning with a lower-case alphabetical character. In addition, words in variable names should employ camelCase rather than underscores (_) or hypens (-).
Note it's not necessary to use Hungarian notation with object properties.
Though it's legal to leave off a trailing semi-colon after a statement, doing so can sometimes mask errors. Always end statements with a semi-colon; this includes object, array and function literals.
//object literal; var oDefaults = { async: true, type: "get", data: "json", success: function (sResponse) { ... } }; //function literal; var fnGetQuote = function (sStock) { ... };
Both operands and operators should be separated by a space, i.e.:
var iTotal = 5 + 2; var iMod = 5 % 2;
Compound statements, that is multiple statements enclosed in curly braces or brackets { }, should always have their opening bracket on the same line that begins the compound statement. The closing bracket should be on its own line and lined up with the first character of the statement that began it.
do { ... } while (condition); //also note the semi-colon (only for do-while compound statements);
if StatementsThe if statement should have all clauses bracketed, i.e.:
if (condition) { ... } else if (condition) { ... } else (condition) { ... }
It is sometimes extremely useful to use the ternary operator instead:
var bReturn = obj instanceof Person ? true : false;
switch StatementsIt is acceptable to allow for fall-throughs, but clarify this in a comment so the reader doesn't "correct" the code:
switch (true) { case oElem.nodeName === "DIV": break; case oElem.nodeName === "INPUT": //falls through; ... case oElem.nodeName === "TEXTAREA": ... break; default: ... }
with StatementsIt's best to avoid with statements as they can be very inefficient and confusing. Using this statement adds another object to the front of the scope chain and can lead to unintended consequences and poor performance as the interpreter traverses the scope chain when doing variable lookups.
Most often this is used as a shorthand for object properties, but instead it's better to stuff the object into a variable. For example, instead of...
//JSLITE.state.manager.myObj.x; //JSLITE.state.manager.myObj.y; with (JSLITE.state.manager.myObj) { var x = x, y = y; }
...do...
var o = JSLITE.state.manager.myObj, x = o.x, y = o.y;
with and eval. It's trite but bears repeating: "If eval is the answer, you're asking the wrong question." Be aware that using the Function constructor and passing strings to setTimeout and setInterval all are evaluated, and their use should be scrupulously avoided. For the latter, pass either an anonymous function or a named variable as the function argument.var and return statement.//only evaluate the length of the collection once by creating a local variable 'iLen' instead of evaluating it each time through the loop; for (var i = 0, iLen = cElems.length; i < iLen; i++) { ... }
innerHTML when doing large DOM updates.