Skip to content

Latest commit

 

History

History
executable file
·
1208 lines (868 loc) · 22.4 KB

README.md

File metadata and controls

executable file
·
1208 lines (868 loc) · 22.4 KB

JavaScript

Saiku JavaScript code styleguide.

Table of Contents

  1. Semicolons
  2. Strict mode
  3. Variables
  4. Prototypes
  5. Regular Expressions
  6. Objects
  7. Properties
  8. Arrays
  9. Strings
  10. Numbers
  11. Functions
  12. Equality comparisons
  13. Conditionals
  14. Blocks
  15. Console Statements
  16. Comments
  17. Naming conventions
  18. Whitespace
  19. Type checking
  20. jQuery
  21. Code linting
  22. Resources

Semicolons

  • 1.1 Always use semicolons.

ASI can be tricky, don't rely on it.

  • 1.2 Don't start function blocks nor anonymous self-invoking functions with semicolons.
// Bad
;(function() {
  console.log('I\'m too cool for school');
})();

// Good
(function() {
  console.log('I\'m awesome');
})();

⬆ back to top

Strict mode

  • 2.1 Make use of the strict mode pragma.

Just be aware not to use it globally.

// Bad (pragma is being used globally)
'use strict';

function doSomething() {
}

function doSomethingElse() {
}

// Good
function doSomething() {
  'use strict';
  // stuff
}

// Good
(function() {
  'use strict';

  function doSomething() {
  }

  function doSomethingElse() {
  }
})();

⬆ back to top

Variables

  • 3.1 Always use var to declare variables.

Not doing so will result in global variables. We want to avoid polluting the global namespace.

// Bad
superPower = new SuperPower();

// Good
var superPower = new SuperPower();
  • 3.2 Use one var declaration per variable.

This allows for better debuggability and avoid some annoying problems.

// Bad
var foo = 1,
    bar = 2,
    baz = 3;

// Good
var foo = 1;
var bar = 2;
var baz = 3;
  • 3.3 Declare unassigned variables last.

This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.

// Bad
var foo = 1;
var bar;
var baz;
var number = 42;

// Good
var foo = 1;
var number = 42;
var bar;
var baz;
  • 3.4 Assign variables at the top of their scope.

This helps avoid issues with variable declaration and assignment hoisting related issues.

// Bad
function() {
  test();
  console.log('Doing stuff...');

  // ...

  var name = getName();

  if (name === 'test') {
    return false;
  }

  return name;
}

// Good
function() {
  var name = getName();

  test();
  console.log('Doing stuff...');

  // ...

  if (name === 'test') {
    return false;
  }

  return name;
}

// Bad (unnecessary function call)
function() {
  var name = getName();

  if (!arguments.length) {
    return false;
  }

  this.setFirstName(name);

  return true;
}

// Good
function() {
  var name;

  if (!arguments.length) {
    return false;
  }

  name = getName();
  this.setFirstName(name);

  return true;
}
  • 3.5 Use || to define default values.
// Bad
function setNumber(value) {
  if (!value) {
    value = 42;
  }
  
  return value;
}

// Good
function setNumber(value) {
  return value || 42;
}

⬆ back to top

Prototypes

  • 4.1 Hacking native prototypes should be avoided at all costs, use a method instead.
// Bad
String.prototype.half = function () {
  return this.substr(0, this.length / 2);
};

// Good
function half(text) {
  return text.substr(0, text.length / 2);
}
  • 4.2 Don't instantiate an object unless you assign it to a variable and use it.

Not doing so configures a code smell and means that the constructor should be replaced with a function that doesn't require new to be used.

// Bad
new Person();

// Bad (`person` is never used)
var person = new Person();

// Good
Person();

// Good
Person.create();

⬆ back to top

Regular Expressions

  • 5.1 Keep regular expressions in variables, don't use them inline.

This will vastly improve readability.

// Bad
if (/\d+/.test(text)) {
  console.log('So many numbers!');
}

// Good
var matchNumbers = /\d+/;

if (matchNumbers.test(text)) {
  console.log('So many numbers!');
}

⬆ back to top

Objects

  • 6.1 Use the literal syntax for object creation.
// Bad
var foo = new Object();

// Good
var bar = {};

They can cause problems in older versions of IE if not wrapped within quotes.

// Bad
var foo = {
  class: 'Foo',
  default: true
};

// Good
var baz = {
  type: 'Baz',
  isDefault: true
};
  • 6.3 Use camelCase when naming object keys.

This way is easier to use dot notation to access object values.

// Bad
var user = {
  'name': 'John',
  'has-children': false,
  'is-underage': false,
  'age': 42
};

// Good
var user = {
  name: 'John',
  hasChildren: false,
  isUnderage: false,
  age: 42
};

⬆ back to top

Properties

  • 7.1 Use dot notation to access properties.

Dot notation is simpler and enforces camelCase when naming object keys.

var user = {
  name: 'John',
  age: 42
};

// Bad
var userName = user['name'];

// Good
var userAge = user.age;

⬆ back to top

Arrays

  • 8.1 Use the literal syntax for array creation.
// Bad
var foo = new Array();

// Good
var bar = [1, 2, 3];
  • 8.2 Use Array#push instead of direct assignment to add items to an array.
var groceries = [];

// Bad
groceries[groceries.length] = 'tomato';

// Good
groceries.push('oreo');
  • 8.3 To cleanup an array set its length to zero.
// Bad
var foo = [1, 3, 5];
foo = null;

// Good
var bar = [2, 4, 6];
bar.length = 0;
  • 8.4 Use Array#slice to clone an array.
var total = items.length;
var itemsCopy = [];

// Bad
for (var i = 0; i < total; i++) {
  itemsCopy[i] = items[i];
}

// Good
itemsCopy = items.slice();
  • 8.5 To convert an array-like object to an array, use Array#slice.
function argsToArray() {
  return Array.prototype.slice.call(arguments);
}

⬆ back to top

Strings

  • 9.1 Use single quotes ' for strings.
// Bad
var reject = "My cat";

// Good
var god = 'My dog';
  • 9.2 Concatenate strings using the plus += operator.

Operators are faster than function calls.

var name = 'Breno';

// Bad
name.concat('Polanski');

// Good
name += ' Polanski';
  • 9.3 Prefer string concatenation for long strings, always adding line breaks after an operator.

Long strings can impact performance big time (benchmark and discussion).

// Bad
var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

// Bad
var errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';

// Good
var errorMessage = 'This is a super long error that was thrown because ' +
  'of Batman. When you stop to think about how Batman had anything to do ' +
  'with this, you would get nowhere fast.';
  • 9.4 Use toString() to convert a value to String.

Be aware that toString() does not work with null and undefined values.

var value = 42;

// Bad
value += ''; // '42'

// Good
value.toString(); // '42'

⬆ back to top

Numbers

  • 10.1 Do not use floating decimals.

Althought they're valid JavaScript, they make the code harder to read.

// Bad
var foo = .5;
var bar = -.7;
var baz = 2.;

// Good
var foo = 0.5;
var bar = -0.7;
var baz = 2.0;

⬆ back to top

Functions

// Bad
var sum = function(x, y) {
  return x + y;
};

// Good
function sum(x, y) {
  return x + y;
}
  • 11.2 Favor named function expression instead of anonymous function expression.

This helps you to debug your code, since the dev tools will show the name of the context which the error has ocurred.

// Bad
var anonymous = function() {}

// Good
var named = function named() {}
  • 11.3 Do not declare a function in a non-function block.
// Just don't
while(true) {
  function test() {
    return false;
  }
}
  • 11.4 Do not name a parameter arguments.

This will take precedence over the arguments object that is given to every function scope.

// Bad
function nope(name, options, arguments) {
  // ...
}

// Good
function yup(name, options, args) {
  // ...
}
  • 11.5 Whenever you have more than 3 arguments being passed to a function use an object instead.
// Bad

function setUser(firstName, lastName, age, gender, occupation) {
  // Too many function arguments
}

setUser('Jon', 'Snow', 25, 'Male', 'Night watcher');

// Good

// Use a config/options object instead
function setUser(user) {
  // Do stuff
}

setUser({
  firstName: 'Jon',
  lastName: 'Snow',
  age: 25,
  gender: 'Male',
  occupation: 'Night watcher'
});
  • 11.6 Use Function() as no-op.
function(callback) {
  setTimeout(callback || Function(), 2000);
}
  • 11.7 Do not modify function parameter values.

Often, assignment to function parameters is unintended and indicative of a mistake or programmer error.

// Bad
function foo(bar) {
  bar = 13;
}

// Good
function foo(bar) {
  var baz = bar;
}
  • 11.8 Make use of bind when you need to persist the value of this accross different scopes.

It removes the need of creating a new variable.

// Bad
function foo() {
  var self = this;
  function bar() {
    self.something();
  }
}

// Good
function foo() {
  function bar() {
    this.something();
  }.bind(this);
}

⬆ back to top

Equality comparisons

  • 12.1 Use strict equality to compare variable values.

Strict equality checks for both value and type which is why we expect.

// Bad
function isEmptyString (text) {
  return text == '';
}

isEmptyString(0);
// <- true

// Good
function isEmptyString (text) {
  return text === '';
}

isEmptyString(0);
// <- false

⬆ back to top

Conditionals

  • 13.1 Ternary operators should only be used to compare booleans.
// Bad
items.length ? show() : hide();

// Really bad (I have no idea what's happening here)
items.length === 0 && settings.id === 'foo' ? hide() : show();

// Good
var isEmpty = items.length === 0;
var isFoo = settings.id === 'foo';

isEmpty && isFoo ? hide() : show();

// Even better
var shouldHide = isEmpty && isFoo;

shouldHide ? hide() : show();
  • 13.2 Don't use double negation !! to test booleans.
// Bad
if (!!foo) {
  // ...
}

// Good
if (foo) {
  // ...
}

Blocks

  • 14.1 Always wrap blocks within braces and embrace new lines.
// Bad
if (false)
  return;

// Bad (wrapped with braces but missing new line)
if (false) { return false; }

// Good
if (true) {
  return true;
}

// Good
if (true) {
  return true;
} 
else {
  return false;
}

// Bad
while(true)
  doSomething();
	
// Good
while(true) {
  doSomething();
}

⬆ back to top

Console Statements

  • 15.1 Preferably bake console statements into a service that can easily be disabled in production. Alternatively, don't ship any console.log printing statements to production distributions.

⬆ back to top

Comments

  • 16.1 Ensure your code is descriptive, well commented, and approachable by others. Great code comments convey context or purpose.

  • 16.2 Using FIXME and TODO tags can help other developers understand and maintain your code. In multiline comments, add a line break and place them at the end of the comment.

  • 16.3 Use documentation block syntax for multiline comments.

  • 16.4 Use // for single line comments. Place them on a newline above the subject of the comment and add an empty line before the comment.

// Bad

/*
Used to match `RegExp` special characters.
See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
for more details.

TODO: Let the web service handle the validation.
FIXME: Bad performance.
*/

// Good

/**
* Used to match `RegExp` special characters.
* See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
* for more details.
*
* TODO: Let the web service handle the validation.
* FIXME: Bad performance.
*/

var matchSpecialChars = /[.*+?^${}()|[\]\/\\]/g;

// Bad (vague description and placed aside of variable declaration)

var isActive = true; // When is active

// Good (right position and descriptive comment)

// Boolean value to indicate whether the current session is active or not
var isActive = true;

// Bad (weird big chunk of code, everything is visually tied together)

function bootstrap() {
  // Notifies if there's a session already running
  if(isActive) {
  	console.log('Session is already active!');
  }
  isActive = true;
  // Return session id so mediator can notify the controller
  return sessionId;
}

// Good

function bootstrap() {

  // Notifies if there's a session already running
  if(isActive) {
  	console.log('Session is already active!');
  }
  
  isActive = true;
  
  // Return session id so mediator can notify the controller
  return sessionId;
}

⬆ back to top

Naming conventions

  • 17.1 Use PascalCase when naming constructors.
// Bad
function crewMember(name, role) {
  this.name = name;
  this.role = role;
}

var god = new crewMember('Tom Barber', 'Developer');

// Good
function CrewMember(name, role) {
  this.name = name;
  this.role = role;
}

var designer = new CrewMember('Tom Barber', 'Developer');
  • 17.2 Avoid single letter names and abbreviations. Be descriptive and clear.
// Bad
function f() {
  // What the hell is this?
}

// Good
function bootstrapFoo() {
  // Ah, ok!
}

// Bad
function stuff() {
  // "stuff" is too generic
}

// Good
function doAnimationStuff() {
  // Now we're talking
}

// Bad (abbreviations)
var props = {};
var config = {};

// Better
var properties = {};
var settings = {};

// Not so good
function init() {
}

// Better
function initialize() {
}
  • 17.3 Always close constructor invocations with parenthesis.

It's going to be easier to pass new constructor values if needed in the future.

// Bad
var foo = new FooBar;

// Good
var bar = new FooBar();
var baz = new FooBar(1, 'lorem');
  • 17.4 Always use a leading underscore _ when naming private properties and methods.
// Bad
var $name = 'Foo';
var __name = 'Bar';

// Good
var _name = 'Baz';
  • 17.5 Booleans should start with "is", "has", or "should".

This give us a clear idea of what that variable is.

// Bad
var ready = true;
var animate = true;
var animation = true;

// Good
var isReady = true;
var shouldAnimate = true;
var hasAnimation = true;
  • 17.6 When naming an acessor, start with get or set. Also always name the getter argument as value.
var currentStatus;

// Bad
function status(val) {
  if (val) {
    currentStatus = val;
  }

  return currentStatus;
}

// Good
function setStatus(value) {
  currentStatus = value;
}

function getStatus() {
  return currentStatus;
}
  • 17.7 When naming an event handler, prefix on along with its event type.
// Bad
$('.button').click(click);

function click() {
  console.log('meh');
}

// Good
$('.button').click(onClick);

function onClick() {
  // Stuff happens here
}
  • 17.8 Use uppercase when naming "constant likes" variables.
// Bad
var esc_key = 27;
var rightKey = 39;

// Good
var ESC_KEY = 27;
var RIGHT_KEY = 39;

⬆ back to top

Whitespace

  • 18.1 Use soft tabs set to 2 spaces and never mix spaces with tabs.
// Bad
function() {
∙∙∙∙var name;
}

// Bad
function() {
∙var name;
}

// Bad
function() {
⇥var name;
}

// Good
function() {
∙∙var name;
}
  • 18.2 Always add an empty line at the end of your file.
(function() {
  document.write('Hello World!');
})();

  • 18.3 Place a space before and after conditions and loop declarations.
// Bad
if(false){return false;}

// Good
if (true) {
  return true;
}

// Bad
while(false){console.log('whatever');}

// Good
while (false) {
  console.log('alright!');
}
  • 18.4 Set off operators with spaces.
// Bad
var x=y+5;

// Good
var x = y + 5;
  • 18.5 Place a space after loop steps.
// Bad
for(var i=0;i<10;i++) {
  console.log(i);
}

// Good
for (var i = 0; i < 42; i++) {
  console.log(i);
}
  • 18.6 Place a space after each function argument.
// Bad
function setUser(name,surname,age){
}

// Good
function setUser(name, surname, age) {
}
  • 18.7 Objects properties should be split into new lines.
// Bad
var user = {name: 'John', surname: 'Doe', age: 42};

// Good
var user = {
  name: 'John',
  surname: 'Doe',
  age: 42
};

// Bad
var setup = { foo: 'bar' };

// Good
var setup = {
  foo: 'bar'
}
  • 18.8 Use indentation when making long method chains.
// Bad
$('.js-items').find('.is-selected').highlight().end().find('.open').updateCount();

// Good
$('.js-items')
  .find('.is-selected')
    .highlight()
    .end()
  .find('.open')
    .updateCount();

⬆ back to top

Type checking

The typeof operator returns a string indicating the type of the unevaluated operand.

typeof variable === 'string';
typeof variable === 'number';
typeof variable === 'boolean';
typeof variable === 'object';
Array.isArray(arrayLikeObject);
elem.nodeType === 1;
variable === null;
typeof variable === 'undefined';

⬆ back to top

jQuery

  • 20.1 Always cache jQuery lookups.
// Bad
$('.js-item').text('Disabled item');
$('.js-item').addClass('is-disabled');

// Good
var $item = $('.js-item');

$item
  .text('Disabled item')
  .addClass('is-disabled');
  • 20.2 Prefer remove() over empty().

remove() is faster because it doesn't completely rewrite the DOM node.

  • 20.3 Always favor jQuery helpers over third-party and custom stuff.
// Bad (importing Underscore/LoDash just to use `_.bind()`)
_.bind(someFunction, this);

// Good (instead use jQuery's `$.proxy()`)
$.proxy(someOtherFunction, this);

// Bad (writing `trim()` from scratch)
function trim(string) {
  return string.replace(/\s/gm, '');
}

trim('   f oo  '); // 'foo'

// Good (instead use jQuery's `$.trim()`)
$.trim('   f oo  '); // 'foo'

⬆ back to top

Code linting

We use ESLint to lint our JavaScript code. All the rules can be found on the .eslintrc file.

⬆ back to top

Resources

Inspiration

⬅ back to main    ⬆ back to top