Image for post
Image for post

NOT ONLY JavaScript Coding & Best Practices Style Guide

During my daily JavaScript development I see a lot of code written by myself and other people that should be better written in a different style. Here I collect some of my personal suggestions to improve these code snippets.

Naming of variables, functions, …

Name variables, functions etc. as specific as possible.

This improves the readability of your code for you and other people.

function find(id) {
...
}
function findCustomer(customerId) {
...
}

Conditions for if, while, …

Create boolean variables to name conditions in if, while, ….

This improves the readability of your code for you and other people.

if (index > 0 && index < maxLen && person.age > 18) {
...
}
var isPersonAdult = person.age > 18;
var isIndexValid = index > 0 && index < maxLen;
if (isIndexValid && isPersonAdult) {
...
}

Check parameters

Because JavaScript allows you to call a function with any parameter, it seems to be a good practice to check these.

Ignoring or simply returning hides errors.

function addCustomer(customer) {
if(!customer) {
return;
}
...
}
function addCustomer(customer) {
console.assert(!!customer, 'customer not given');
console.assert(customer instance of Customer, 'no costumer object given');
...
}

Comment where a comment is needed

Don’t bore with comments that are obvious, instead comment the not so obvious things.

Boring demotivates and secrets decreses consciousness power. Always ask yourself: Is this really obvious or could it be misunderstood?

//sets customerId to '4'
var id = '4';
...
scope.tabIndex = (value) ? "-1" : "0";
var customerId = '4';
...
//ignore control during tabbing if disabled (-1), or handle
//tabbing in source order (0) otherwise
scope.tabIndex = (disabled) ? "-1" : "0";

Provide variables with default values.

Sometimes I find code that has to check if a variable is defined and if not it is initialised with i.e. an empty array.

You have to write additonal code to check the variable each time you want to use it.

var selectedCustomerIds;
...
if (!selectedCustomerIds) {
selectedCustomerIds = [];
}
selectedCustomerIds.forEach(...);
var selectedCustomerIds = [];
...
selectedCustomerIds.forEach(...);

Replace switch case by a dictionary

If you are simply replacing one constant value through another constant value, it is easier to use a object for that.

Makes your code simpler and faster.

var lockFlagText;switch (lockFlag) {
case 'l':
lockFlagText = 'Lost';
break;
case 'd':
lockFlagText = 'Defect';
break;
case 'f':
lockFlagText = 'Fired';
break;
case ' ':
lockFlagText = 'Not locked';
break;
}
var LOCK_FLAG_TEXTS = {'l': 'Lost', 'd': 'Defect', f: 'Fired', ' ': 'Not locked'};
...
var lockFlagText = LOCK_FLAG_TEXTS[lockFlag];

Avoid anonymous functions

It is a common practice to use anonymous function declarations for defining a callback or something else. Try to avoid this and instead name each function explicitly.

During debugging it is annoying to read anonymous function, because that does not help to understand what is going on.

You also had to name it, if you want to programm a recursive function, calling itself inside.

var controller.setEventHandler( function () {
...
}
var controller.setEventHandler( function handleCustomerIdChangedEvent() {
...
}

Crash early

It’s always a bad idea to silently catch exceptions or not executing a function when a given parameter is missing. You should have good arguments when doing so.

Simply because you are hiding errors in using the function or executing code. This does not help to get your program free of errors and even worse hides the source of the problem poping up somewhere else.

function saveCustomer(customerEntity)  {
if(!customerEntity) {
return;
}
try {
resource.update(customerEntity);
}
catch (e) {
//error occured
}
}
function saveCustomer(customerEntity)  {
console.assert(customerEntity, 'customerEntity not given.');
...
try {
resource.update(customerEntity);
}
catch (exception) {
console.log(exception); //or create business exception or ...
throw exception;
}
}

Use constants

Use properly named constants instead magic values spreaded all over the code.

The code is more readable for you and others and also robust to changes, because the change happens at exactly one point in the source.

...
var tax = price * 0.19;
var TAX_RATE = 0.19; //19%
...
var tax = price * TAX_RATE;

And even better with ECMAScript 2015 use const ;-)

Avoid extending build in types

I recently extended an array to contain additional data. Why? Because it is possible and I was to lazy to move it to an object containing an array with the additional data.

But don`t do that or at least keep in mind what could happen.

Everybody else is expecting a simple array, and so maybe does not see the additonal attribute. So does angular.equals ;-)

var customerIds = [ 1, 2, 3, 4 ];
customerIds.lastSelectedId = 4;
var customerSelection = { 
lastSelectedId: 4,
ids: [ 1, 2, 3, 4 ]
};
Example?

Avoid parameter hell

To much parameters given to a function or method makes the code unreadable or even difficult to read. User parameter objects to avoid that.

First the code is more readable, because you are naming the parameters where they are created and used for the call. Even better, the signature of the function does not change over time you add new parameters. Old calls still work, no change needed.

function calculatePerformance(isAdult, hasChildren, alive, worksHard, knowsEverything, countsAlot, moneyToSpend, dayOfSelection, isImportant) {
...
}
...
calculatePerformance( true, false, true, true, false, true, 1234.56, day, true);
function calculatePerformance(dayOfSelection, moneyToSpend, options) {
...
}
var options = {
isAdult: true,
hasChildren: false,
alive: true,
worksHard: true,
knowsEverything: false,
countsAlot: true,
isImportant: true
};
calculatePerformance(day, 1234.56, options);

Use parentheses

Even if you know the exact operator precedence, always use parantheses to explicit define the order of evaluation a statement.

For the reader of your code it is annoying to think about the evaluation order. Also if you do use parentheses, there is no reasoning, whether you missed the parantheses or not.

var a = 4 * 3 + 2;
var a = (4 * 3) + 2;

Don’t create mammoth functions?

Today I discovered a function that was able to do multiple actions for various input constellations. It uses ‘if then else’ constructs to distinguish the purpose. I was wondering, why the author did not add more specific functions, that only does one thing, and so could be easy and clear named.

Those functions are hard to read an maintain. Be as concrete as possible to make the code read as a novel, explaning what is going on. If you can’t name the exact purpose of a function you probabily mixing things up, that does not belong together.

function iCanDoEverything(type, data) {
if (type === 1) {
//iDoThis
...
} else if (type === 2) {
//iDoThat
...
} ...
}
function iDoThis(data) {
...
}
function iDoThat(otherData) {
...
}

Use simple function parameters

Sometimes we are lazy and put big objects into the signature of a function to avoid more than one parameter or we do not see the general purpose of the function. That’s most of the time not a good choice.

  • It makes the function useful only in a few cases where you hold this object in your hands.
  • It increases dependencies and therefore does not welcome changes.
  • It can not be used in different scenarios with different objects.
function calculateAge(customer) {
var today = Date.now();
var birthday = customer.dateOfBirth;
// return something like:
// today - birthday;
...
}
var aCustomer = {
id: '4711',
name: 'Joe Dear',
dateOfBirth: new Date(...)
};
console.log(calculateAge(aCustomer));
function calculateAge(dateOfBirth) {
var today = Date.now();
...
}
var aCustomer = {
id: '4711',
name: 'Joe Dear',
dateOfBirth: new Date(...)
};
console.log(calculateAge(aCustomer.dateOfBirth));

Use parentheses more often

Every now and than I trap into the pitfall not to use parantheses in calculations and the like. This sometimes lead to hard to find errors, that simply can be avoided by always using parantheses.

The reader and author has not to imagine what is expected here and does not have to know the operand precedence.

var a = 4 + true == true ? 42 : 0; //sets a to 0 instead of 46
var a = 4 + (true == true ? 42 : 0); //sets a to 46

What?

Name your object attributes without context unless it makes sense to distinguish them from others on the same level.

Dont bore you collegues with repeating the context that should already be clear.

rack.rackWidth
rack.width

What?

Make an API consistent and do not handle edge cases in another way the user does not expect. E.g. returning an element instead of an array with this element.

Last time I consumed an API using ODATA I was surprised that it handles the case of returning only one element of metadate different from returning multiple entries. For the first case it returns simply an element, in the latter case it returns an array of elements.

As a consumer of an API it is not so nice to handle all these edge cases with almost no win for both sides and it is easy to overlook those cases, causing errors.

return result.length === 1 ? result[0] : result
return result

What?

Do not change a container while you are iterating it.

Adding or removing items of a container during an iteration could b a bad idea. What should the interpreter do? Ignore those elements or process them? So to be sure to never change a container during iterating through the same container. Maybe you come never through.

for (const type of allTypes) {
if (condition) {
allTypes.push(42)
}
}
for (const type of allTypes) {
if (condition) {
newTypes.push(42)
}
}
allTypes = allTypes.concat(newTypes)

Comments, adjustments and extensions are welcome ;-)

Developer since the 80'th, working at Forschungszentrum Jülich (http://www.fz-juelich.de) …

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store