- Variables
- Strings
- Operators
- Arrays
- Objects
- Functions
- Iteration
- Error Handling
- Libraries
- NPM
- Documentation and Style
- Code Blocks
Default to const
. If you need to change the value later on, then you can use let
. Do not use var
ever
This ensures that you can't reassign your references, which can lead to bugs and difficult to comprehend code.
Do not use const
or let
.
Avoids issues with hoisting and temporal dead zones
//bad
function foo() {
this.year = 1993;
const foo = 'bar';
this.baz = foo;
}
//good
function foo() {
const foo = 'bar';
this.year = 1993;
this.baz = foo;
}
Simplify changeset diffs, reduce risk of creating global variables, helps with breakpoints.
//very bad
function foo() {
const foo = 1, bar = 2, baz = 3;
}
//bad
function foo() {
const foo = 1,
bar = 2,
baz = 3;
}
//good
function foo() {
const foo = 'bar';
const bar = 2;
const baz = 3;
}
To avoid complications...
//bad
function sheNever() {
const foo = 1;
let bar = 2;
const baz = 3;
let bae = 4;
}
//good
function keptTheSameAddress() {
const foo = 1;
const baz = 3;
let bar = 2;
let bae = 4;
}
Use lowerCamelCase for naming variables and functions. Use UpperCamelCase only for naming classes.
Why? Consistency makes for a cleaner code base. Classes are treated differently because they require the
new
keyword.
//bad
let OptimusPrime = true;
const ENERGONCUBES = 1;
function Transform() { console.log('Roll out!'); }
class autobot { }
//good
let optimusPrime = true;
const energonCubes = 1;
function transform() { console.log('Roll out!'); }
class Autobot { }
This ensures readability in strings where variables are inserted
Always use string literals when concatenating strings together.
//bad
const string = 'The ASH UI team is ' + awesomeAdjective + '!';
//good
const string = `The ASH UI team is ${awesomeAdjective}!`;
There is no hard standard for using single quotes, double quotes, or tick marks.
- Use tick marks only with template literals.
- Strictly use double quotes with JSON.
Example:
//good
const string = 'The ASH UI team is awesome!';
//also good
const string = "The ASH UI team is awesome!";
//bad
const string = `The ASH UI team is awesome!`;
Default to Strict Equality, ===
. Use ==
only for special cases, such as when you don't care about the type. When using ==
comment code explaining your choice.
//bad
if(1 == '1') // evaluates to true
//good
if('5' === 5) // evaluates to false
if(5 === 5) // evaluates to true
There is no hard standard for dangling commas.
Yes it looks ugly and weird but it has benefits in simplifying diffs.
//ok
const arr = [
'foo',
'bar',
];
const obj = {
hey: true,
jude: true,
};
//also ok
const arr = [
'foo',
'bar'
];
const obj = {
hey: true,
jude: true
};
When able to code in ES6, use shorthand method declarations. Otherwise normal method declarations are OK.
//bad unless stuck in ES5
const obj = {
doThisThing: function() {
return true;
}
};
//good (but only in ES6)
const obj = {
doThisThing() {
return true;
}
};
There is no hard standard for dangling commas.
Yes it looks ugly and weird but it has benefits in simplifying diffs.
//ok
const arr = [
'foo',
'bar',
];
const obj = {
hey: true,
jude: true,
};
//also ok
const arr = [
'foo',
'bar'
];
const obj = {
hey: true,
jude: true
};
Arrow functions cut down and have special properties in terms of lexical scoping.
This preserves the value of this
which removes the need for you to "save" the value of this (commonly as that
or self
).
const myObject = {
init() {
const that = this;
someFunctionWithCallback(1, function() {
console.log(this);
//logs the Window object. Most likely not the behavior you are expecting. You have use the "that" variable we initiated up there.
console.log(that);
});
},
init2() {
someFunctionWithCallback(1, () => {
console.log(this);
//logs myObject!
});
}
}
When there is only one parameter, do not encase it in parenthesis.
//bad
let itemsLength = items.map((item)=>{
return item.length;
});
//good
let itemsLength = items.map( item =>{
return item.length;
});
Use normal syntax for things like event listeners where do not want to preserve this
.
// This is a special exception.
//bad
thing.on('click', e => {
$(this).addClass('newClass');
//this will NOT work
});
//good
thing.on('click', function(e) {
$(this).addClass('newClass');
});
When working with large datasets, use the classic for
loop as it allows you to break;
out of the iteration, which prevents the expense of looping through unnecessary items after a certain criteria has been met. You cannot do this with iterative methods
const array = [1, 2, 3, ... 10,000];
const newArray = [];
// Bad
array.forEach((item, index) => {
if (index < 50) {
newArray.push(item * 3);
}
});
// Good
for (let i = 0; i < array.length; i++) {
if (i === 50) {
break;
}
newArray.push(array[i] * 3);
}
Errors happen. If an error blocks or inhibits the user flow, we should always display the error to the user. You can
console.error()
errors if useful for developers, but do not rely on this for end users. If using Ember, you can use thedebug
orwarn
methods, which get stripped out in production (see @ember/debug for more info).
Helps catch errors before they cause the code to blow up
Use try/catch block if:
- You are attempting to access an object that is more than two levels deep
- You are even slightly uncertain that the object structure may vary
Tips
- Make sure that the catch block of the try/catch does something meaningful for the user
- If you need a fail-safe error message to display as a result of the catch in a try/catch, talk to your PO for the message content
//bad
function showErrorMessage (arg1){
//this will result in Uncaught TypeError: Cannot read property 'responseJSON' of undefined
error = arg1.response.responseJSON.responseStatus;
return error;
}
Executing something after try catch: Use finally to execute that code rather than rewrite the code twice or only write for the happier (try) path.
//good
function showErrorMessage (arg1){
try {
error = arg1.response.responseJSON.responseStatus;
} catch (e) {
error = 'Something went wrong fetching the response'; //in this case, e is undefined so you don't need it on the line above
//OR
error = e;
} finally {
removeLoader();
return error;
}
}
Anytime you're doing something with a successful resolution of an asynchronous operation (happy path), you should ensure you are also catching errors (sad path).
Use .catch()
to catch any errors with the promise, including http errors.
//bad
const list = [...staleList];
fetchNewestItem().then(response =>{
list.push(response);
})
//good
const list = [...staleList];
fetchNewestItem().then(response =>{
list.push(response);
}).catch(error =>{
displayErrorToUser(error);
})
Similar to Promise.then()
, include a catch, but use a try/catch statement for async
functions.
//bad
const list = [...staleList];
(async function (){
list.push(await fetchNewestItem())
})()
//good
(async function (){
try{
list.push(await fetchNewestItem())
}catch(er ror){
displayErrorToUser(error)
}
})()
Why? jQuery is a huge library, and no longer necessary for cross-browser compatibility. Writing in vanilla javascript will also give you a deeper understanding of the core language
Note: When updating legacy code to use vanilla javascript, be mindful of which objects are passed to 3rd party plugins or in-house plugins that still use jQuery, and wrap the object in a jQuery wrapper accordingly
- Update the Major version when making a breaking API changes.
- Update the Minor version when introducing a new feature.
- Update the Patch version when adding a bug fix.
- Always start on a minor version 0.1.0.
- Do not publish to the registry until the code can deliver functionality, tested, and can be consumed by other applications.
- If the code is being used on production, then it should be at least on version 1.0.0.
Why? Because other devs need to be able to work in your code without setting up a meeting first.
While there isn't a hard standard, a good rule of thumb is if you need to explain what it's doing to the next developer, you should add documentation. You can document a block at the top of a function or tell a story as developers step through your code. Don't document obvious things.
//BAD
function isPalindrome(word){
// create and set the newWord variable
let newWord = word.replace(/[^a-zA-Z0-9]/g, '');
newWord = newWord.toLowerCase();
// return true if one of the conditions are true
if(newWord.length == 0 || newWord.length == 1){
return true;
}
if(newWord === newWord.split('').reverse('').join('')){
return true;
}
// return false
return false;
}
// Good
// Check if a given word/string is palindrome
function isPalindrome(word){
// strip all characters and symbols except a-z, A-Z, and 0-9
let newWord = word.replace(/[^a-zA-Z0-9]/g, '');
newWord = newWord.toLowerCase();
if(newWord.length === 0 || newWord.length == 1){
return true;
}
// check the original word against the new reversed string
if(newWord == newWord.split('').reverse('').join('')){
return true;
}
return false;
}
- Good code is readable by other developers.
- Good code can do complex things.
- Code that can do complex things, but is not readable by other developers is not good code.
It is allowed, but not required, to use single line code blocks without curly braces ({}
) as long as they fit on the same line as the control statement (ex: if
, for
). When the code becomes more than 1 statement and/or moves to the next line, it should be placed in curly braces.
Why? This allows for simple
if
statements to be terse, while providing a hint for anyone modifying it that they need to add curly braces for additional statements.
//BAD
if (iceCream > vegetables)
return true;
for (let i=0; i<vegetables.length; i++)
disposeOf(vegetables[i]);
//GOOD
if (iceCream > vegetables) return true;
if (iceCream > vegetables) {
return true;
}
for (let i=0; i<vegetables.length; i++) disposeOf(vegetables[i]);
for (let i=0; i<vegetables.length; i++) {
disposeOf(vegetables[i]);
}