last revised March 18, 2011
effective starting March 18, 2011
Introduction
Overview
Comments
File Naming Conventions
Constants
Variables
Functions
Blocks
Security
Error Handling
Modules
Libraries
Sample Script
This document describes source code standards for Javascript, both as included directly in Web pages and as standalone include files, developed at Cliffson Solutions, LLC. Unless superseded by client requirements, these standards apply to all Javascript code developed by employees and subcontractors of this company for both internal use and client projects.
The purpose of these standards is to bring consistency to the structure of Javascript code, including formatting and naming conventions. The Javascript interpreter allows a large latitude in how the source code is organized. Although this flexibility has benefits in requiring only a relatively few, simple grammar rules, if some rules for structure and convention are not applied, the code can become very difficult to read and maintain.
Most clients only care if the code does what it is designed to do, not how it is formatted. However, the practical consideration is that well-formatted, well-documented, and well-organized code is considerably easier to maintain and enhance, saving both the client and developers time and money in the long run. Given that a Web-based script may require on average as much as four times the original development time in debugging, maintenance, and feature tweaks over its lifetime, an investment in creating good source code initially is easily paid back in the long run. Moreover, applying some discipline to how the code is organized, particularly in variable naming conventions, tends to encourage better logic and fewer side-effects and bugs.
We readily acknowledge that many of the conventions described here are strictly preferences and that coding formats and naming conventions can incite passions on par with religious convictions. However, the conventions described here have been developed based on years of writing code and noting what works and what does not. In some cases these may result in a bit more typing, but any programmer worth his/her salt will 1) already be a decent typist, and 2) realizes clear coding can save at least an order of magnitude in time quickly locating functional blocks and debugging activities.
This document discusses Javascript as it supports Web page functionality. The corresponding documents Style Guide & Coding Standards for HTML and Style Guide & Coding Standards for CSS" should also be consulted.
In general, Javascript code segments should always appear in the <head> section of the Web page or in separate files referenced and included in the <head> block. Using Javascript statements in the <body> section should be avoided as much as possible, with rare exceptions for one-line codes associated with form element events (such as onchange or onclick) or inline seal verification handling (such as SSL certificates). Indiscriminately scattering Javascript code throughout the entire HTML document makes debugging extremely cumbersome and difficult.
Javascript code is always delimeted using <script type="text/javascript"> ... <script> tags. The use of <script language="Javascript"> ... <script> is deprecated and should not be used.
We have adopted a slightly modified Allman-BSD format for block formatting, the slight modification being two-space indents instead of the traditional four spaces (two space are sufficient to visually indicate blocks without using an inordinate amount of horizontal real estate). This means that both open and close curly brackets that delineate blocks appear on lines by themselves. Although the starting curly bracket works just as well at the end of the line preceeding the block, putting the opening bracket on an extra line by itself is well-worth the effort in readability. See the section Blocks below for more details.
We also use one-letter prefixes on all variable names to indicate their types. Several other naming schemes and even other programming languages also do this, and the benefit to keeping track if a variable is a string or an integer inherently in its name is well-worth the time taken to type an extra letter each time it is referenced. Javascript's automatic typecasting is a bit iffy, so using these prefixes are very helpful in keeping variables straight.
Using multiple indices in nested loops is not nearly as confusing if each index is named after its use. See Variables below for more details.
All code is written assuming a 80-column display. Long lines that are longer than 80 characters must be broken into multiple lines at the logical locations. For example, a long assignment statement may be broken into two lines at the = operator. Long strings may be broken into several pieces by using the concatenation operator (+). Calls to functions with a long list of parameters may be broken between any of the arguments and list continued on the next line.
Javascript allows the use of both /* ... */ and // as comment delimeters. Because the /* ... */ is easily applied to disable large chunks of code, we reserve its use for that purpose and encourage the use of multiple asterisks to draw attention to it. The // prefix is used to initiate comments, with each comment line requiring that prefix.
Comments that start with // are indented to the current level of code indenting.
Example:
function fnTest(itest) { // this is a test function that returns the square of the supplied // int; if error, return 0 // sanity checks /***************************** /* disabled for now if (itest <= 0)) return 0; *****************************/ return itest * itest; }
Individual files containing Javascript which included in HTML or PHP code are always named with .js extensions. Such files are used when a number of pages need access to the same Javascript code, particularly functions for form validation and AJAX calls. They may also be used if the amount of Javascript code is substantial and breaking it out from the main page code aids readability or reduces complexity. If Javascript code includes constants defined by PHP statements, the Javascript code must be included in the .php file, as .js files are not processed by the PHP interpreter.
We define three types of headers: headers at the top of separate code files, headers that separate major blocks of code, and function headers.
File Headers:
Individual .js files containing Javascript code for inclusion in Web pages must include file headers. The lines of these headers are delimeted with double slashes (//), and they must contain the following:
- file name, including relative path if in a subdirectory of the main project directory
- the purpose of the file and the code it contains
- dependencies on other files, if any
- the original creation date, author, and affiliation
- the last revision date, author, and affiliation
An example of a file header:
////////////////////////////////////////////////////////////////////////// // test.js // // // // a set of Javascript utility functions to support conference // // registrations, including credit card validation and enforcement // // of mandatory elements; refer to comments in each function // // definition for detailed input, output, and behavior of each // // // // Written 2/27/2011, L.Pritchett, Cliffson Solutions, LLC // // Revised 2/27/2011, L.Pritchett, Cliffson Solutions, LLC // //////////////////////////////////////////////////////////////////////////
Dates should be in the format shown above, with four-digit years. Blank lines are used to improve readability.
During initial development of the code, if only a single programmer is making changes, a single revision line with a current date is sufficient. If more than one programmer is working on the same file, each programmer should have a separate revision line, with the latest revision line always appearing last. Details as to the types of revisions made are not required, as during a rapidly evolving project those types of notes should be entered into the project management system or added as the code is checked back into the software repository.
Once the code has gone into production, the last pre-release revision lines should be left intact and new revision lines should be added. At this point each revision line should include summary comments as to the changes made.
Section Headers:
Section headers are simply comment blocks that delineate major blocks of code, including:
- global constants
- global variables
- types of functions (such as "input validation", "output functions", "error handling", "pre-submission handling")
- inline code (executed when the page is loaded, although these are better handled in functions and invoked as onload events)
Section headers are used primarily as search aides, as well as to keeping different blocks of code from simply flowing into one another. To make them stand out visually, they include a long line of = characters to cover the width of the page.
Example:
//========================================================================= // global constants and variables //=========================================================================
If appropriate, subheaders may also be used, built from + characters. Subheaders may be useful if a large number of functions are defined but may be grouped by type.
Example:
//========================================================================= // functions //========================================================================= //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // input functions //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function Headers:
Function headers are basically the comments that start each function definition. Unless the function is obvious and trivial, these headers are required immediately after the curly bracket starting the function definition and and before the local variable declarations (or first line of code, if no local variables are used).
These headers should include the purpose of the function, expected inputs, return values, any effects on global Javascript variables and page elements, and the behavior upon encountering errors.
Example:
function fnSquare(icount) { // accept positive integer and return its square; if variable is invalid, // report as alert() and return 0 // sanity checks if ((typeof(icount) === "undefined") || (icount <= 0)) { alert("invalid parm passed to fnSquare()"); return 0; } return icount * icount; }
Javascript does not really provide for immutable constants like some other languages. However, constant values can be defined as assignments to global variables immediately after the <script> tag and before any code appears.
So-called "magic numbers" (numbers that are basically constants) should not hardcoded into functions, but should be collected as a set of global constants. The rationale for this is easier future updates if those values change: rather than search for all possible references to a particular value, it can be changed in one place. This should be done not only for numbers which can reasonably be expected to change over time (e.g., number of employees), but also for any other situtation-dependent value even if those are not anticipated to change (e.g., number of vice presidents) — because at some point they may.
All constants should include the same one-letter lowercase prefixes used in variable names (see Variables below), but the rest of the names are in capital letters. All constant names must be descriptive and meaningful. Abbreviations are allowed for the sake of typing and space efficiencies, but the overall name must be clear and not cryptic. Because mixed case is not allowed in constant names, underscores should be used for readability.
Examples:
var sPGM_NAME = "test_program"; var fPI = 3.14159; var iEMPLOYEES = 334;
Using global variables to share values across multiple functions should be avoided if all possible, although globally defined constants are obvious exceptions. Javascript's scope enforcement to prevent unintentional alterations to variables outside functions is fairly weak; this is necessary to allow access to DOM elements, but otherwise can cause major confusion if variables are not carefully tracked and named. All function variables intended to be local must be declared or initialized in var statements to ensure they stay local (leaving this off automatically makes variables global).
To the degree possible, functions that require access to variables and values should be passed those as function parameters. A single DOM element may be passed as part of an event handler (e.g., onchange and onclick) using the self-referential this. Otherwise, access and manipulation of DOM elements, which are cumbersome to handle as function parameters, are more easily referenced within the function using built-in DOM references such as document.getElementById( ).
Cookies generally should not be used, mostly because they have been abused to the point that many users have configured their browsers to block them. Tracking state information on persistent Web sessions, particularly those requiring user authentication, are far more easily, robustly, and securely handled using PHP sessions or similar server-side methods. Javascript variables should never include sensitive data such as plain text passwords.
All variables should be named with descriptive and meaningful designations. Abbreviations are allowed if and only if the overall name is not cryptic. Variable names which are comprised of acronyms are allowed only if the acronyms are commonly recognized (e.g., url, ssl, html); otherwise, a short descriptive name must be used. The only exceptions to these rules are simple loop counters (e.g, i, j, k). However, nested loops should use more descriptive counters to make the loop logic more self-documenting.
All variables must include a one- or two-letter prefix that indicates the variable type or use. These are defined as
i = integer
f = floating point number
s = string
c = char
b = Boolean
a = array
o = object (including form elements)
If global variables must be used, they must include at least one capitalized letter after the one-letter type designator. Mixed case names (CamelCase or camelBack notation), underscores, or a mixture of the two should be used as necessary for readability. All variables defined in a function, either explicitly using var statements or as formal parameters passed to the function, are always lowercase. Adhering to these conventions will help easily identify the nominal scope of a given variable at a glance. Also see the section on Constants above.
Examples:
var iPi; [global integer] var fTotal; [global float] var sFirst_Name; [global string] for (i = 0; i < 10; i++) [simple loop inside function] function fnTest(icount) [integer formal parameter in function prototype] var ftotal; [float defined in function] var surl; [string defined in function] var ofirst_name; [form element defined in function]
All variables must be explicitly initialized before use.
All functions names are prefixed with fn, followed by a capital letter, followed by mixed-case characters, numbers, and/or underscores. This naming convention is consistent with using mixed-case names for global variables, as functions are typically global in scope. All function names must be meaningful and descriptive, and should avoid acronyms that are not commonly known. For example, fnURL_Parse_String( ) is okay, but fnGDIC_ADC_Handler( ) is not particularly descriptive. Functions which may be used in conditional statements should be named consistently with the values they return.
Example:
if (!fnVariable_Exists(stest)) ...
Ideally, all functions should validate their incoming parameters, both that the parameter is defined (not null, unless this is a valid possibility) and that values are within expected ranges. However, Javascript functions are typically used in very restricted environments, and unless the functions are defined as part of a permanent library of Javascript routines, such tests may be optional. Obviously, if the purpose of a function is to validate user input, checking of variables is inherent in the function anyway. Also see the section Error Handling below.
Unlike C and some other programming languages, Javascript functions gracefully handle strings of arbitrary lengths and not easily subject to buffer overflows. However, checking for maliciously large values against reasonable upper limits is a good precaution. Such checks are required if the value will be passed as with an AJAX call) to an external program that does have length limitations (such as a compiled C utility). Careful note should be taken of the limits of Javascript integers and inherent rounding errors associated with floating point variables.
Functions may return single values of any type, depending on what they are intended to do. Functions which return Boolean values may return either true or false, depending on what makes sense with the name of the function. For example, fnVar_Exists( ) would return true if the specified variable is found, but fnVar_Missing( ) would return false in the same circumstance. This demonstrates the importance of picking meaningful function names, particularly if those are used in conditional statements (if, while, etc.).
Returning multiple values from functions may be accomplished by returning arrays or by directly modifying global program variables (although the use of global variables should be limited as much as possible).
All program blocks are indented by two spaces per level. This applies to global variables, function definitions, and inline code within <script> ... </script> tags, as well as logical blocks of code within functions. Tabs are never used, as they do not always render consistently across different terminal software (this is for the sake of editors, as the browsers don't care). Editors which handle indents as an optimal mix of tabs (assuming tabs are equivalent to eight characters) and spaces must have this feature configured to use spaces only or this feature must be disabled.
Curly brackets which start blocks always appear on lines by themselves, indented to the level equal to the statement immediately preceeding the block. Ending curly brackets also appear on lines by themselves and are indented to the same level as the corresponding opening bracket.
Example:
function fnTest() { var icount; for (icount = 0; icount < 10; icount++) { alert("interation #" + icount); document.getElementById("sMsg").innerHTML = "advancing ..."; } return; }
All statements which are longer than 80 characters after indenting must be manually wrapped to fit in an 80-column display. Breaks in such lines should be made where convenient (e.g., at commas, logical operators such as && or ||, and other natural breaks). Long strings with no natural breaks will need to broken into multiple assignment statements or use the concatenation operator (+) to break them into two or more pieces. Continuation lines should always be indented an additional two spaces until the entire logical line is complete.
Example:
smessage = "This is an example of a really long line that just " + "never seems to end: not! After all, now is the time for all " + "good computers to come to the aid of their owners.";
Note that single-line blocks do not require curly brackets unless the statement immediately above is broken into continuation lines which are already indented two spaces. In that case, adding curly brackets is required for readability.
Example:
for (i = 0; i < 10; i++) itotal += i;
Example:
if (bFirst_Name_Found && bLast_Name_Found && bHighSchool_Grad && bEmployed && (cGender == 'M')) { alert("Found match on criteria"); }
The blocks corresponding to case( ) statements are indented by two spaces and otherwise handled the same as any other block except they do not require start and stop curly brackets.
Example:
switch (ioption) { case 1: alert("starting script."; break; case 2: alert("script is ending."; break; default: alert("invalid option specified"; }
All built-in functions such as if, for, and while are separated by one space from their opening parentheses. All characters representing logical, mathematical, and concatenation operators are separated from their surrounding arguments by one space for readability.
Example:
if ((iline < 10) && !bdone) { $iline++; smessage = "reading character #" + (iline * 60); }
Example:
alert("Error encountered: " . sMsg . "\nAborting!");
Sensitive information, including passwords or database credentials, are never stored in Javascript constants or in cookies. If such values need to be passed between pages or manipulated as stateful controls, they should be handled by server-side scripts such as PHP.
Sensitive data, including personally identifiable information (PII), user authentication credentials, and credit card information, are always transmitted on SSL connections using industry-standard encryption and certificate validation technology. All HTTPS connections must use registered (not self-signed) certificates from licensed commercial Certificate Authorities. All Web forms which require sensitive information must include a link to the site's privacy policy or privacy statement, as well as a clickable seal from the Certificate Authority to validate the current site's SSL certificate. For more information, refer to our Privacy Policy and Security Policy documents.
Because Javascript is client-side programming and completely visible to users who care to look, it is also susceptible to malicious tinkering. Users can easily save a local copy of a Web page and modify the Javascript code to bypass input validation and even force form values outside expected ranges. While Javascript can generate dynamic forms with quick validation and page modifications based on user interaction, it should never be relied on solely to provide valid input back to the server.
All Web form inputs must be checked for missing values, out-of-range values, over-length values (buffer overflows), self-consistency of form options, SQL injection attacks, and the like by the server-side processing scripts or programs. All input is considered to be suspect until validated in this fashion. Calculated dollar amounts, in particular, must be recalculated and verified on the server. For more information, refer to the Security sections of the Style Guide & Coding Standards for C/C++ and Style Guide & Coding Standards for PHP documents.
If cookies or session variables are used to track system logins and access controls, the ultimate control of those variables must be on the server, not in Javascript. Using Javascript to block viewing of sensitive material or URLs based on network addresses, browser IDs, or cookies is not allowed. Access to such material should always be controlled at the server, and such material should never be included in hidden layers under the control of Javascript functions.
Access and handling of PII, credit card information, and other sensitive data is strictly controlled. Details of how this data is handled is in our Privacy Policy document.
For more information regarding security, also refer to our Security Policy document.
Errors may be handled directly by the section of the function that encounters the problem, or by a specialized error reporting function. The latter is preferable, because the format of the error message may be easily modified in one place. Most commonly client-side errors are reported by alert( ) messages. Alternatively, hidden layers (<div> blocks) may be enabled to display appropriate embedded messages in the page. The use of pop-up (child) windows is not allowed, given that many users these days have their browsers configured to block pop-up windows and will never see such messages.
Severe errors that reflect problems with server data or serious attempts by a user to bypass input checking may also cause a page to initiate a self-submission to specialized server program or script which logs and reports to the problem.
Messages should always be meaningful and never just include an error number. Error numbers may be included to help the programmer quickly locate the problem, although a better approach is to include the name of the function in which the error occurred in the message.
A generic error function should accept the name of the caller and a string specifying the type of error. Care must be taken to not include sensitive information (e.g., PII or credit card information) in such messages.
If the error is not fatal, the script should gracefully report the problem and give the user an opportunity to fix the issue. If the problem is more serious (e.g, a failed AJAX call), the user should be notified that the problem has occurred, what is being done about it, and what the user's next step is. The user should always be left knowing what happened and that someone has been notified about the problem. Error reporting pages should never be dead ends and should always include links to continue browsing.
Large programming projects commonly split development efforts among multiple programmers working with multiple source code files. All such files are maintained within a single directory on one of our servers, although subdirectories may be created along functional lines to make management and organization easier. All projects use a software repository for backup and version control purposes. All programmers working on such projects will be clearly notified which pieces are their responsibility and how function interfaces are expected to work.
All external Javascript files, including common library utility files, are named with .js extensions. All such files are included in the target Web page with <script type="text/javascript" src=" ____ .js"></script> tags.
Commonly used functions with applicability to multiple projects may be gathered into a centrally compiled and maintained library of external Javascript files. Because the impact of these functions is potentially much larger, all changes to function prototypes, calculations, return values, and error handling must be carefully reviewed by the management of Cliffson Solutions before being implemented. All such changes must be thoroughly documented and tested against all programs known to use the affected functions.
Only the Manager of Applications Development has the authority to update working libraries.
The following is an example of a typical external Javascript file demonstrating all major expected components in code developed at Cliffson Solutions, LLC.
////////////////////////////////////////////////////////////////////////// // registration_utils.js // // // // [purpose of contents of this file]; [dependencies]; [expected // // inputs];[expected outputs];[other relevant notes regarding its // // operation or expected usage] // // // // [identification of client or final destination] // // // // Written [mm/dd/yyyy], [author] // // Revised [mm/dd/yyyy], [author] // ////////////////////////////////////////////////////////////////////////// //======================================================================== // general utility functions //======================================================================== function fnStr_LeftTrim(oinput) { // trims whitespace from start of string oinput.value = oinput.value.replace(/^\s+/g, ''); } function fnStr_Trim(oinput) { // trims whitespace from start and end of string oinput.value = oinput.value.replace(/^\s+|\s+$/g, ''); } //======================================================================== // validation function //======================================================================== function fnUpdate_State() { // update state of "submit" button based on validity of input fields // called by individual field validation functions, in turn invoked // as "onkeyup" or "onchange" events var oform = document.frmRegistration; var bok = (oForm.sTitle_Other.value.length && oForm.sFirst.value.length && oForm.sLast.value.length && oForm.sAddr_Street.value.length && oForm.sAddr_City.value.length && oForm.sAddr_State.value.length && oForm.sAddr_ZIP.value.length); // update state of submit button and missing-values error message oform.bttnSubmit.disabled = !bok; document.getElementById("divMissgVal").style.display = !bok ? "block" : "none"; return bok; }
The following is an example of typical Javascript code included in a PHP or HTML file.
<script type="text/javascript"> //==================================================================== // global constants //==================================================================== iSLEEP_LENGTH 30 //======================================================================== // general utility functions //======================================================================== function fnStr_LeftTrim(oinput) { // trims whitespace from start of string oinput.value = oinput.value.replace(/^\s+/g, ''); } function fnStr_Trim(oinput) { // trims whitespace from start and end of string oinput.value = oinput.value.replace(/^\s+|\s+$/g, ''); } //======================================================================== // validation function //======================================================================== function fnUpdate_State(oform) { // update state of "submit" button based on validity of input fields // called by individual field validation functions, in turn invoked // as "onkeyup" or "onchange" events var bok = (oForm.sTitle_Other.value.length && oForm.sFirst.value.length && oForm.sLast.value.length && oForm.sAddr_Street.value.length && oForm.sAddr_City.value.length && oForm.sAddr_State.value.length && oForm.sAddr_ZIP.value.length); // update state of submit button and missing-values error message oform.bttnSubmit.disabled = !bok; document.getElementById("divMissgVal").style.display = !bok ? "block" : "none"; return bok; } </script>