Manual:Coding conventions/JavaScript

From MediaWiki.org
Jump to: navigation, search

This page describes the coding conventions used within files of the MediaWiki codebase written in JavaScript. See also the general conventions that apply to all program languages, including JavaScript.

Contents

[edit] Code structure

[edit] Closures

Always wrap your code in a closure. This avoids leakage of variables from or to another module. Use the following closure ("Immediately-Invoked Function Expression"[1]):

( function ( $, mw, undefined ) { 
        // Code here will be invoked immediately
}( jQuery, mediaWiki ) );

When writing a jQuery plugin that doesn't use any MediaWiki variables and want to execute it at the document ready event, you may also use the following format, which removes the need for a separate closure as well as aliasing jQuery to $:

jQuery( document ).ready( function ( $ ) { 
        // This function is called by jQuery internally as fn( jQuery )
} );

[edit] Whitespace

See also the general conventions.

Like CSS declarations, in an object literal the notation should be one key per line, have no space before the colon and one space after the colon.

Use operators such as typeof like typeof foobar, not like a function typeof( foobar ).

[edit] Example code

Instead of endlessly describing all conventions, below are a few sample codes that demonstrate the following:

  • the order in which code should generally be structured
  • the use and placement of function and var statements (all var statements at the top of the function scope)
  • Use literal arrays and objects
  • Use compare to undefined instead of executing a typeof operation and comparing strings.
  • No Yoda conditional[2]
/*jslint browser: true, white: true*/
/*global mediaWiki, jQuery*/
/**
 * Example module for MediaWiki
 */
( function ( $, mw, undefined ) {
        "use strict";
        var foo, bar, quux, cache, ids;
 
        function convertFooToBar( foo, bar ) {
                /* ... */
        }
 
        foo = mw.msg( 'foo' );
        bar = mw.util.wikiScript( 'api' );
        quux = mw.config.get( 'quux' );
        cache = {};
        ids = [];
 
        mw.example = {
                ids: ids,
                addIds: function ( add ) {
                        return $.isArray( add ) ? ids.push.apply( ids, add ) : ids.push( add );
                },
                doStuff: function () {
                        /* .. */
                },
                bar: function ( x ) {
                        if ( x === undefined ) {
                                x = bar;
                        }
                        /* .. */
                },
                quux: function ( q ) {
                        if ( arguments.length === 0 ) {
                                return quux;
                        }
                        /* .. */
                        quux = q;
                        return true;
                }
        };
 
}( jQuery, mediaWiki ) );

[edit] Naming

All variables and functions must use lowerCamelCase for their naming. For functions, verb phrases are preferred (so getFoo() instead of foo()).

The only exception to this are object constructors. Due to the nature of JavaScript a function is not declared as an object constructor, instead it is declared like any other and used as an object constructor with the new operator. Since no error checking exists, always use UpperCamelCase for naming constructors so that the required new operator is easily recognized by the reader.

[edit] Pitfalls

  • JavaScript does not have block scope. Declare your variables at the top of every function.
  • Bracket notation on a string ('foobar'[2]) doesn't work in older versions of IE such as IE6. Use the String prototype charAt instead ('foobar'.charAt(2)).
  • Be sure to check the MDN Docs before using a prototype. For example, the filter Array prototype isn't supported in any version of Internet Explorer before 9. See also Compatibility#Browser.
  • Never make an assumption or imply its scope. A variable should be created as either var foo or window.doo. Undeclared variables are assumed to be global - the opposite of PHP.
  • When throwing exceptions, use throw new Error('message') rather than throw 'message'. You can throw a string directly in JavaScript, but some debuggers won't pick up a stack trace or the location of the error without using the Error constructor.
  • Use $.isArray() to cover for edge cases and cross-browser differences (instead of typeof or Array.isArray)
  • Be careful to preserve compatibility with left-to-right and right-to-left languages (ie. float: right or text-align: left), especially when styling text containers. This is another reason why such declarations should be in CSS files, so that they are automagically flipped by CSSJanus in ResourceLoader for RTL-languages.
  • Beware of trailing commas in object literals as they fail in Internet Explorer.
  • Similarly, when using an array literal, don't include any extra commas. In older versions of IE, [ 1, 2, , 3, ] will be interpreted as [ 1, 2, undefined, 3, undefined ] instead of the expected [ 1, 2, 3 ].
  • Make sure you're using attr() or prop() appropriately. Read more at http://javascript.info/tutorial/attributes-and-custom-properties

[edit] jQuery pitfalls

  • Always quote attribute selector values: [foo="bar"] instead of [foo=bar]. Avoid bugs like jqbug 8229.
  • Don't use .size(). It's the same number of characters as .length and the property lookup is faster than a function call plus the property lookup (source of .size()).

[edit] Comments and documentation

Make sure though that you use the JavaScript terminology (such as Number instead of integer).

In older versions of MediaWiki, JavaScript code was often very poorly commented to keep it small. Since MediaWiki 1.17 and higher run code through ResourceLoader's minifiers and the like, please feel free to liberally comment your code to aid future maintainers.

Doxygen/jsdoc documentation is not yet built automatically from JavaScript files, but it may be at some point in the future. In the meantime, some advanced editors like NetBeans can make use of JSDoc-style annotations to aid in autocomplete and code navigation.

Here's the format we aim to follow from now on:

/**
 * Create an outsources Cool object.
 * @constructor
 *
 * @example
 *      new Cool( 5 );
 *
 * @param foo {Number} Total sources.
 */
function Cool( foo ) {
        // ...
}
 
/**
 * Some short description of this function.
 *
 * @example
 *      $( '.foo' ).foobar();
 *
 * @context {jQuery}
 * @return {String} HTML-fragment
 */
$.fn.foobar = function () {
        if ( this.val() === 'baz' ) {
                return this.html();
        }
        return this.html( '<baz>Foo foo foo</baz>' ).html();
}

[edit] jQuery

See also jQuery

As of MediaWiki 1.17 jQuery is loaded by default and available everywhere. familiarize yourself with its API and don't be afraid to use jQuery for DOM manipulation, AJAX requests, and utility functions. These are usually much easier to work with than classic low-level DOM and DHTML interfaces, and much more consistent across browsers.

To avoid confusion with raw elements or other variables, we prefix variables storing instances of jQuery with a dollar symbol. This makes it easy to recognize and manipulate them even with great distance between point X and the point of definition. A common problem is when referring to them in a conditional statement. document.getElementById returns null if no element matched the ID, therefore (since null casts to boolean false) it can be used as-is in an if statement. jQuery objects on the other hand (as any object in JavaScript) cast to boolean true no matter what.

[edit] Performance and best practices

JSHint
Validate with JSHint. Recommended JSHint settings:

  • Tick all "Warn"-checkboxes, except these:
  • [ ] About unsafe line breaks

Assume:

  • [x] Browser
  • [x] jQuery
  • /*global mediaWiki*/

JSLint
If you use JSLint:

  • [x] Assume a browser
  • [x] Tolerate ++ and --
  • [x] Tolerate messy white space ("messy", as in, not using Crockford's conventions. MediaWiki's conventions match his reasoning, just slightly different implementation)
  • Globals: jQuery, mediaWiki

Use CSS for styling many elements
Don't apply styling to lots of elements at once; this has poor performance. Instead use a common parent's class (or add one) and apply CSS in a .css file. Thanks to ResourceLoader, this will all be loaded in the same HTTP request, so there's no performance penalty for having a separate CSS file. Do not set CSS into inline "style" attributes, don't insert "style" elements from JS either.

Dot notation
Use dot-notation whenever possible to access object members.

Literals and native constructors
Use array and object literal notation (var foo = []; var bar = {};), do not use new Object() or new Array(). Do not use new String(), new Number() or new Boolean(). (Usage of String() and Number as conversion functions is encouraged but don't use them as constructors due to equality problems).

[edit] References

  1. http://benalman.com/news/2010/11/immediately-invoked-function-expression/
  2. http://www.globalnerdy.com/wordpress/wp-content/uploads/2010/05/yodaconditional.jpg
Conventions
General All languages · Security for developers · Pre-commit checklist · Style guide (draft)
PHP Code conventions
JavaScript Code conventions · JavaScript performance
CSS Code conventions
Database Code conventions
Personal tools
Namespaces

Variants
Actions
Navigation
Support
Download
Development
Communication
Print/export
Toolbox