Skip to content

Instantly share code, notes, and snippets.

@elwayman02
Created May 27, 2015 22:55
Show Gist options
  • Save elwayman02/969891273f3334d7f52d to your computer and use it in GitHub Desktop.
Save elwayman02/969891273f3334d7f52d to your computer and use it in GitHub Desktop.

This section explains the basic styles and patterns used in our JavaScript code. It borrows heavily from established resources at other companies that have extensive experience with JS, such as Yahoo, Google, and Mozilla. This guide is written with ES6 syntax in mind.

Some interesting JavaScript tips and gotchas can be found here

Comments

Use JS Doc-style comments whenever possible. Google's style guide has a great summary of JSDoc guidelines.

Strings

Our coding style for strings vary depending on the type of document:

ES6/ES2015 JavaScript

Use `` over '' or "". Interpolated strings are a vast improvement in ES6, and now we can use both single and double quotes inside our strings without needing to escape them.

const foo = `My name is "Jordan".`;

ES5 JavaScript

Use '' over "". This is helpful when creating strings that include html.

var foo = '<div class="foo"></div>';

HTML (HTMLbars/Handlebars)

Use "" for HTML attribute strings and '' inside of HTMLbars helpers. This vastly improves readability when helpers are embedded in HTML attributes.

<div class="foo {{if isBar 'bar' 'baz'}}"></div>

Semicolons

Use them. Always. See this reference for an explanation of the dangers of semicolon omission.

Function Declarations/Expressions

While they are largely equivalent in most cases, we prefer Function Expressions over Function Declarations for a couple of reasons.

  • Function Declarations are prohibited within code blocks according to the language spec (even though most browsers support it anyway)
if (x) {
    function foo() {
        // don't do this
    }
}
  • Function Declarations automatically get hoisted, whereas Function Expressions will only have the variable hoisted. This is consistent with the expected behavior in JS hoisting.
// bar gets declared here, but not assigned
bar(); // throws undefined error, as expected
// bat, on the other hand, has its entire function body hoisted
bat(); // will actually work

const bar = function () { /.../ };
function bat() { /.../ }
  • Thanks to hoisting, you cannot conditionally define Function Declarations
if (x) { // foo will get defined regardless of how this condition evaluates
    function foo() { /.../ }
}
if (y) { // bar will be undefined if the conditional fails
    const bar = function () { /.../ };
}
  • The main selling point for Function Declarations is that named functions are surfaced by name in stack traces. However, this can also easily be done with Function Expressions.
const foo = function foo() { /.../ };
const bar = function barFunc() { /.../ }; // You can even give it a different name for stack traces

Else-Return

Don't put an else statement after a return. It's unnecessary and reduces readability.

if (x) {
    // do stuff
    return y;
}
return z;

// don't do this
if (x) {
    // do stuff
    return y;
} else {
    return z;
}

Comparisons

Always use === over == (and !== over !=). Automatic type coercion in JavaScript is sketchy, and if your data is well-formatted, you shouldn't need it. The strict equals is a lot more reliable in its execution.

However don't use === to compare a boolean, or you may encounter unintended side effects.

// Prefer this
if (x) {}
if (!y) {}
// over this
if (x === true) {}
if (y === false) {}

Naming Conventions

Variables

Use lowerCamelCase. In most cases, use const where var would be used in ES5. Restrict usage of let to variables that are expected to change value within the current scope of the code.

let fooBar = 4;
const fooBarTwo = 8;

Constants

Use capitalized names for const variables that serve as "magic numbers" or similar. (Ex: const TIME_DELAY = 200;)

Enums

Use CamelCase. If the enums are keyed from strings, capitalize them since they are constants (see above)

const DaysOfTheWeek = {
    MONDAY: 1,
    TUESDAY: 2,
    /.../
};

Functions

Use lowerCamelCase.

const fooBarFunc = function() {
    // do stuff
};

Constructors

Object constructors should be CamelCased (though this pattern is generally not used in Ember apps, since that has been abstracted away).

function User() {
    // initialize stuff
}
const user = new User();

Model Properties

Use lowerCamelCase.

export default DS.Model.extend({
    'firstName': DS.attr('string')
});

Indenting

2 spaces.

Newlines

Use a blank line to group logically related pieces of code. Never use multiple blank lines together, a single line is sufficient for readability.

let x = 'foo';
doSomethingToX(x);
doSomethingElseToX(x);

let y = 'bar';
/.../

Add a blank line around method declarations, to improve readability. However, don't put a newline above the first method in an indent block or after the last method.

export default Ember.Route.extend({
    model: function () {
        return this.store.find('foo');
    },

    setupController: function () {
        const foo = () => {
            // do stuff
        };

        const bar = () => {
            // do other stuff
        };
        
        /.../
    }
});

Spacing

This section outlines our preferred standard for spacing in various situations:

Functions

Always use a space after a function declaration.

const foo = function () { /.../ };

This makes it easy to tell when a function is named or not.

const foo = function func() { /.../ };

Braces

Braces should have spaces around them, except before commas and semicolons.

const foo = {
    bar: { stuff: 'stuff' },
    reset() {
        // do stuff
    }
};

Conditionals

Use spaces after conditional keywords.

if (foo) { /.../ }
return foo ? 'foo' : 'bar';

Formatting

Braces

Always put an opening brace on the same line, and give a closing brace a new line, unless the contents will be in a single line.

// GOOD
let foo = {
   bar: 'baz'
};
if (foo) {
   // do stuff
}
let foo = { bar: 'baz' };

// BAD
let foo = 
{
    bar: 'baz'
};
if (foo)
{
    //do stuff
}
let foo = { bar: 'baz'
};

Object/Array Initialization

If they are relatively short, objects and arrays may be initialized on a single line. Otherwise, each item should have its own line, indented.

const fooArr = [1, 2, 3];
const fooObj = { bar: 'baz', stuff: 'things' };
const fooLargeArr = [{
    bar: 'baz',
    stuff: 'things',
    reset() {
        // reset obj
    }
}, {
    /.../
}, {
    /.../
}];

The same is true for HTMLbars helpers:

{{my-component classNames='foo bar'}}
{{my-component
    classNames='foo bar'
    foo=foo
    bar=bar}}

Don't attempt to align object assignments, as that pattern breaks down with longer variable names.

// GOOD
const foo = {
    a: 'bar',
    b: 'baz',
    myReallySpecificName: 'stuff'
};
// BAD
const foo = {
    a                   : 'bar',
    b                   : 'baz',
    myReallySpecificName: 'stuff'
};

Function Calls

Don't put the first parameter of a function call on a new line. If you must wrap, either break the call into two lines or put each parameter on its own line.

// BAD
veryLongFunctionNameBeingCalled(
    foo, bar, baz, veryLongParameterVariableName);
// GOOD
veryLongFunctionNameBeingCalled(foo, bar,
    baz, veryLongParameterVariableName);
// Ok, but avoid unless there are a lot of parameters or the variables are all very long
veryLongFunctionNameBeingCalled(
    foo, 
    bar,
    baz, 
    veryLongParameterVariableName
);

Binary, Ternary & Dot Operators

Put these all on one line if possible. If it is too long, either wrap to a second line at one of the operators, or put each operator on its own line.

let foo = foo || 'default value';
if (foo === bar || foo === baz || !foo ||
    someMethodCall(foo)) {

    foo.addItem(bar).chainMe(baz);
    return foo ? foo : bar;
}

foo.removeItem(bar).
    reallyLongChainCall(baz).
    moreChainingMoarFun();
return someReallyLongCheck(foo) ?
    someReallyLongReturn : 
    someOtherReallyLongReturn;

For-in

For-in iterates over all of the keys on an object, so it's not recommended to use it when iterating over an actual array (plus it's less performant). If you find yourself using for-in on an array for any reason, make sure to use hasOwnProperty() within the loop before accessing the property.

Wrappers for Primitive Types

Don't do it. They can cause unintended effects:

const foo = new Boolean(false);
if (foo) { // This evaluates to true!
    alert('foo');
}

Explicit type casting is ok, if necessary:

const foo = Boolean(0);
if (foo) { // This evaluates to false!
    alert('foo');
}

Delete

Prefer this.foo = null over delete this.foo. Reference

Console.log & Debugger

Don't check-in code with console.log or debugger statements. If it's necessary to keep some logging in place after development has completed, utilize the Ember Logger instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment