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
Use JS Doc-style comments whenever possible. Google's style guide has a great summary of JSDoc guidelines.
Our coding style for strings vary depending on the type of document:
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".`;
Use '' over "". This is helpful when creating strings that include html.
var foo = '<div class="foo"></div>';
Use "" for HTML attribute strings and '' inside of HTMLbars helpers. This vastly improves readability when helpers are embedded in HTML attributes.
Use them. Always. See this reference for an explanation of the dangers of semicolon omission.
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
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;
}
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) {}
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;
Use capitalized names for const
variables that serve as "magic numbers" or similar. (Ex: const TIME_DELAY = 200;
)
Use CamelCase. If the enums are keyed from strings, capitalize them since they are constants (see above)
const DaysOfTheWeek = {
MONDAY: 1,
TUESDAY: 2,
/.../
};
Use lowerCamelCase.
const fooBarFunc = function() {
// do stuff
};
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();
Use lowerCamelCase.
export default DS.Model.extend({
'firstName': DS.attr('string')
});
2 spaces.
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
};
/.../
}
});
This section outlines our preferred standard for spacing in various situations:
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 should have spaces around them, except before commas and semicolons.
const foo = {
bar: { stuff: 'stuff' },
reset() {
// do stuff
}
};
Use spaces after conditional keywords.
if (foo) { /.../ }
return foo ? 'foo' : 'bar';
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'
};
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:
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'
};
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
);
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 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.
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');
}
Prefer this.foo = null
over delete this.foo
. Reference
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.