JavaScript
##########
General
=======
* Variable declarations must start with 'var'.
* Try your best to never to use a semicolon.
This means avoiding them at line breaks and avoiding multi-statement lines.
A semi-colon at the end of the line is only added when required.
* Use 4 space indentation.
* Use variables, functions, methods and constants names following PEP8.
variable_name, function_name, methodName, ClassName, CONSTANT_NAME
* Callback methods should always end with 'Callback' as the
method / function name.
This is a reminder of the fact that the context is lost.
* Constructor functions (those that are called with **new**) should use
class naming convention.
* use ``===`` instead of ``==`` for comparison. ``===`` doesn't do type
coercion.
* Only use ``typeof`` for checking against undefined variables::
typeof foo !== 'undefined'
* For all other type checking use the custom defined typeOf() function.
* Try to avoid the usage of ``instanceof`` and don't use it for native types.
* Create new arrays using the array literals ``[]`` notation. Don't use
``new Array()``.
* Iterate through arrays using the classic ``for`` loop::
var list = [1, 2, 3, 4, 5, ...... 100000000];
for (var i = 0, l = list.length; i == l; i++) {
console.log(list[i]);
}
* Always define the variable used to iterate into the ``for`` loop first statement.
* Don't use ``eval`` and don't pass strings to ``setTimeout`` and
``setInterval``.
* Use ``undefined`` instead of the the traditional ``null``.
* Keep braces on the same line as their corresponding statements. Never omit
them for single-line if / else statements.
.. sourcecode:: javascript
if (something) {
// code here
} else {
// code here
}
function duppy() {
var something
return {
foo: function() {}
}
}
* Leave 2 empty lines between global function or class definitions.
Leave 1 empty line between functions from the same class.
Global variables can be grouped and the don't require empty lines in between them.
.. sourcecode:: javascript
var GLOBAL_DAY = 1
var GLOBAL_YEAR = 3
/*
Docstring for duppy.
*/
function duppy() {
// Do something here.
}
/*
Docstring for duppy_doo.
*/
function duppy_doo() {
// Do something here.
}
/*
A simple mock for Browser service.
*/
function MockBrowser() {
this._cookies = {}
/*
Docstring for MockBrower.setCookies()
*/
this.setCookie = function(name, value) {
// Implementation here.
}
/*
Docstring here.
*/
this.doSomethingElse = function(name) {
// Implementation here.
}
this._methodWihoutDocstring = function(name) {
// Implementation here.
}
}
* Avoid using leading parenthesis.
* Avoid using the ``delete`` operator and only use it to delete explicitly
set properties on normal objects:
.. sourcecode:: javascript
var obj = {x: 1};
obj.y = 2;
delete obj.x; // true
delete obj.y; // true
* Use single quote for strings.
Example::
In HTML, we use " as quotes around attribute values, like this:
.. sourcecode:: html
bar
In JavaScript, we use ' as much as possible.
alert('qux');
This way, we can use consistent quotes when writing HTML inside of JS:
alert('bar')
* REST web services should always return a valid dictionary
and not an Array or a primitive.
JSON-RPC is forced to return a dictionary by the protocol.
* Callbacks called from the GUI / DOM should be prefixed with `on`.
Ex: onAuthentication (when authenticate button is pressed),
onLogout (when logout link is pressed),
onLoginFormSubmit (when login form is submitted)
* Callbacks/Errback for XHR are be prefixed with `cb` and `eb`, similar
with Python/Twisted convention.
* For one line comments, leave one empty space after the comment marker.
.. sourcecode:: javascript
// Good comment line.
//Bad comment line.
* For multi line comments use the following convention.
.. sourcecode:: javascript
/*
Short single line comment title.
More details about what is here and
here and here.
Feel free to create paragraphs separation.
*/
* Global constants will follow the CONSTANT_NAME naming convention.
* Global services (objects with methods) are named similar to class names.
Most of the time they will be singletons so there will be no associated
class.
.. sourcecode:: javascript
var Shell = new ActiveXObject("WScript.Shell");
function do_something() {
var bla = Shell.method_usage()
}
TODO
* http://javascript.crockford.com/code.html
* http://jibbering.com/faq/notes/code-guidelines/
Prevent polluting the global scope
==================================
You can use immediately invoked function expression IIFE to avoid
injecting more variables into global scope.
When using IIFE don't forget to add the semicolon at the beginning.
.. sourcecode:: javascript
// Path something from global scope.
;(function () {
// tagsInput is kept only inside this scope.
var tagsInput = angular.module('ngTagsInput')
tagsInput.factory('tiTranscludeAppendDirective', function() {
return function() {}
})
})()
Defining classes
================
In JS there is no strict way of defining a class and instances are created
using a function and new operator.
When defining a class we use an anonymous function to allow class
private instances and create a new class scope.
.. sourcecode:: javascript
var BaseAccount = (function() {
var class_private_member = 2
/*
Constructor is here.
*/
var cls = function(name, age) {
this.name = name
this.age = age
}
cls.prototype.class_member = 3
/*
Base method.
*/
cls.prototype.base_method = function() {
return this.name + '-' + this.age
}
/*
Some method.
*/
cls.prototype.some_method = function(prefix) {
return prefix + this.base_method()
}
/*
Another method.
*/
cls.prototype.tuned = function() {
return false
}
return cls
}())
var SpecialAccount = (function() {
var cls = function(name, age) {
this.variant = 'light'
/* Something similar to super()*/
BaseAccount.call(this, name, age)
}
/* Something similar to inheritance. */
cls.prototype = Object.create(BaseAccount.prototype)
cls.prototype.constructor = cls
/*
Method extending parent.
*/
cls.prototype.some_method = function(prefix) {
var parent = BaseAccount.prototype.some_method.call(this, prefix)
return prefix + '-child-' + parent
}
/*
Method overwriting parent.
*/
cls.prototype.tuned = function() {
return true
}
return cls
}())
CSS interaction
===============
Use ``js-CSS-CLASS-NAME`` for any CSS classes that are used from JS.
Use ``test-CSS-CLASS-NAME`` for any CSS classes that are used in the testing
code.
Make sure these classes have no CSS properties.
More info on our :doc:`CSS ` page.
Don't modify the associated CSS properties or HTML attribute.
Rather modify the CSS class:
.. sourcecode:: javascript
// Good
$('#element_id').addClass('highlight')
$('#element_id').addClass('sprite red_dot')
// Bad
$('#element_id').css('font-weight': 'bold')
$('#element_id').attr('src': 'some/red_dot.png')
Test styleguide
===============
* We use ``expect`` style testing.
* Leave 2 emtpy lines before each ``suite`` and one empty line before each
``test``
.. sourcecode:: javascript
/*
Tests for login controller.
*/
suite('LoginCtrl', function() {
// Shared variables.
var scope
var ctrl
setup(function() {
// Initialize first.
})
teardown(function() {
// Clean second.
})
test(
'Initializes with no errors and blank values' +
'long line are wrapped',
function() {
var something = Something()
something.doSomething()
assert.equal('', something.username)
})
suite('critical_error attribute', function(){
test(
'When set, hides the form and sets the error message.',
function(){
var message = manu.makeUniqueString()
scope.critical_error = message
scope.$digest()
assert.isFalse(scope.show_form)
assert.equal(message, scope.alert.error)
})
})
})
Rerefences
----------
Here are the pages I used to create this page:
* http://toranbillups.com/blog/archive/2013/05/15/Basic-javascript-inheritance-and-polymorphism/