How to Write JavaScript the Right Way (With Examples)

Adem Hodzic

Written by: Adem Hodzic

Learn to write better JavaScript with examples!

JavaScript is my favourite language of all time. It’s easy to learn but hard to master language.

Because it’s hard to master, today I am presenting to you 9 ways to make your JavaScript look more professional.

Use let and const instead of var keyword

One thing that you will see in every JavaScript style guide is not to use var keyword for declaring variables.

Variables declared with var will be hoisted to the top of the function scope. That means it will be visible at the top of the scope where you reference it. Did you lose me? Maybe this can help.

  var answer = 42; // This is what we see
  var answer; 
  answer = 42; // This is what the computer sees

One more issue is the ability to declare the same identifier more than one time. That can cause a lot of issues in large codebases or if you are reusing the same variable names.

let and const prevent this. They are designed to be block-scoped which means they belong to the scope where they are declared. I think this is best demonstrated with some good old code.

Here is a quick demonstration. What do you think will be the output of this piece of code?

let arr = [];

(function() {
  for (var i = 0; i < 3; i++) {
    arr.push(function() {return i})
  }
}
)();

arr.forEach(func => console.log(func()))

That’s right! It’s [3,3,3]. Wait, what? This doesn’t make any sense. It’s not obvious but remember that I just said that var hoists variable declaration to the top of the scope? Here is what actually happened?

let arr = [];

(function() {
  var i;
  for (i = 0; i < 3; i++) {
    arr.push(function() {return i})
  }
}
)();

arr.forEach(func => console.log(func()))

But wait? Shouldn’t arr contain three functions where first returns 1, second 2 and third 3?

No.

Why?

Because it’s referencing i in function scope not i in the for loop scope.

How can we fix this?

Change var to let.

let arr = [];

(function() {
  for (let i = 0; i < 3; i++) {
    arr.push(function() {return i})
  }
}
)();

arr.forEach(func => console.log(func()))

Now we get [1,2,3].

If var hoists variable declaration to the top of the function, what will happen if we use it in global scope?

Excellent question. It will be attached to the window object. Can you now see if for e.g. you install some library and it attaches animation variable to window and somewhere else you declare variable animation? Long story short, it would be a nightmare to debug.

In conclusion, if you plan on not changing the value of a variable you should declare it with const. If you will change the value of the variable you should use let.

Note that mutating properties of an object or adding and removing elements from the array will not be considered changing the value since array and object are reference type so use const there too.

Use arrow function syntax.

Before we start talking about my favourite feature of ES6 also known as Ecmascript2015, I need to tell you the difference between function expression and function statement.


  const functionExpression = function() {
    return 'Function Expression';
  }

  function functionStatement() {
    return 'Function Statement';
  }

  console.log('This is', functionExpression());
  console.log('This is', functionStatement());

They look the same right? Well, almost. You just need to remember that function statements are hoisted and function expressions are not hoisted. And function expression have name property but that’s not too important.

With Ecmascript2015 we got a new way to write our function expressions. Many developers think that arrow functions are just syntactic sugar for function keyword but that’s not the case.

First, let’s get over some of the forms of new arrow functions with few examples.


const addTwoES5 = function(number) {
  return number + 2;
}

const addTwoES6 = (number) => {
  return number + 2;
}

Well this doesn’t look that cleaner, doesn’t it? Why don’t we trim some of the parts we don’t need?

  const addTwoES6 = number => number + 2;

How about now? With arrow functions, we have implicit returns in cases where we need just one line to write it.

How would we return an object? I’m glad you asked. Here is how.

  const giveMeACat = () => ({name: 'Lazarus', cuteness: 9001});

One more way to use arrow function without arguments is to put _ instead of ().

This looks cool but why should I bother?

Have you ever had a problem this keyword? No? Then you should start learning JavaScript. It’s an awesome language.

Arrow functions don’t have their own this. Unlike functions declared with function keyword where this keyword refers to the scope of that function, arrow functions retain this of the caller’s scope.

Let’s say you have Pets object with two properties called owner and names and you want to implement pet method. We don’t want to complicate things so we write it like this.

  const Pets = {
    owner: 'Kodeblok',
    names: ['Lazarus', 'Gopher'],
    pet: function() {
      console.log(this.owner + ' is petting ' + this.names)
    }
  }

  Pets.pet();

OK, nothing too strange. We get Kodeblok is petting Lazarus, Gopher as expected. But let’s give each of the pet some more attention. Let’s say the owner wants to give attention to every of its pets. No problemo. We just need some refactoring magic and we are all set.

  const Pets = {
    owner: 'Kodeblok',
    names: ['Lazarus', 'Gopher'],
    pet: function() {
      this.names.forEach(function (name) {
        console.log(this.owner + ' is petting ' + name)
      })
    }
  }

  Pets.pet();

Aaand this.owner is undefined. That’s because this is referring to anonymous functions this not Pet this.

Arrow functions to the rescue!

  const Pets = {
    owner: 'Kodeblok',
    names: ['Lazarus', 'Gopher'],
    pet: function() {
      this.names.forEach(name => console.log(this.owner + ' is petting ' + name))
    }
  }

  Pets.pet();

And now everyone is happy. Yay!

Should I always use arrow functions?

Of course not. For e.g., if we used arrow function with our first example we would get undefined. You can try it if you want.

You must use functions statements for object methods, generator functions or if you want to attach a function on window object.

But use arrow functions for everything else.

== is not equal to ===

== is an equal operator and we use it to compare values.

Pretty simple, right?

So, 77 == 77 will return true because they are same? Correct.

That means 77 == '77' should be false because 77 is a number and '77' is a string, right? Well, no.

You see, when we pass two values of a different type to == operator, it tries to cast them to a common type. In this case it can cast '77' in 77 so we get true.

=== operator prevents this. === is strict comparison operator. It will check if both the value AND type are the same.

To keep this section short, just always use === because there is no reason to use ==.

Learn to Iterate the Right Way

Even if you don’t know JavaScript, if you ever programmed in your life you are probably familiar with the concept of for loop.

Before ES6+ we used for loops to iterate over arrays like this:

  const someArray = ['Hello', 'World', "!"];
  for (let i = 0; i < 3; i++) {
    console.log(someArray[i]);
  }

But there are more ways to the same thing and make your code look more idiomatic.

Let’s start with forEach() method.

forEach is an array method that takes a callback function and passes each array element to that callback. It can also pass two more arguments: the index of the current element and array that we are currently iterating over.

So our previous example will look something like this:


  const someArray = ['Hello', 'World', "!"];
  someArray.forEach(element => console.log(element));

  // Or if you want it all on one line

  ['Hello', 'World', "!"].forEach(element => console.log(element));

This does look cleaner, doesn’t it?

One more benefit with this approach is that we are eliminating off-by-one error.

Next up we have for...in and for...of. For more than I am willing to admit I thought these two were the same thing.

The for…in statement iterates over the enumerable properties of an object, in an arbitrary order.

The for…of statement iterates over values that the iterable object defines to be iterated over.

Translated in English this means that we are using for...in to iterate over properties of an object (remember that Array is also Object) and for...of over values of an iterable.

I didn’t mention this up to now but iterables can be anything that contains Symbol.iterator. This can be a post all for itself so for simplicity sake when I write iterable think array, okay?

Here is an example that should make these concepts a bit clearer:


  Array.prototype.hotel = 'Trivago'; 

  const importantNumbers = [43,69,420];

  for (let number in importantNumbers) {
    console.log(number); // 0, 1, 2, hotel
  }

  for (let number of importantNumbers) {
    console.log(number); // 43, 69, 420
  }

With for...in we iterated over properties of Array object. You can see how this can be useful when we need to iterate over keys of an object.

for...of just iterated over values of our iterable. For those who know what an iterator is it just called iterator.next() bunch of times until internal done variable wasn’t true.

So, when should you use what method for iteration?

Use forEach when you need to iterate over the whole array and use for...of when you need to use continue or break somewhere. And of course use for and for...in when you have a good reason to but I’d stay away from them.

Use async/await If You Need to Nest Promises

If you ever had a pleasure to work with callbacks you will know that Promise API was sent from heaven. But Promises still can get messy from time to time. Like this for example:

const firstPromise = new Promise((res, rej) => res());
const secondPromise = new Promise((res, rej) => res());
const thirdPromise = new Promise((res, rej) => res());

firstPromise
  .then(() => {
    secondPromise.then(() => {
      thirdPromise.then(() => {
        console.log('Hello, World!')
      })
    })
  })
  .catch(err => console.error(err));

Introducing async/await syntax. Next-generation of asynchronous JavaScript syntax.

async is a keyword that we put in front of a function in which we want to use await keyword. I have to point out that async functions will always return a Promise even if it’s a void function.

await keyword is used in an async function to declare variables whose values are resolved values of other Promises. That means the function will block until Promise that we are awaiting (see what I did there) to resolve.

That turns our previous example in this:

const firstPromise = new Promise((res, rej) => res());
const secondPromise = new Promise((res, rej) => res());
const thirdPromise = new Promise((res, rej) => res());

// Remember, although this is a void funtion, it will
// still return a Promise.
async function theRightWay() { 
  const firstResult = await firstPromise;
  const secondResult = await secondPromise;
  const thirdResult = await thirdPromise;
  console.log('Hello, World!');
}

theRightWay()
  .then(() => {})
  .catch(err => console.error(err));

Much more readable than before right?

If it’s possible that some of the promises that we are awaiting to throw an Error, we should then wrap our code in try/catch block.

const firstPromise = new Promise((res, rej) => res());
const secondPromise = new Promise((res, rej) => res());
const thirdPromise = new Promise((res, rej) => res());

async function theRightWay() { 
  try {
    const firstResult = await firstPromise;
    const secondResult = await secondPromise;
    const thirdResult = await thirdPromise;
  } catch(err) {
    throw err; // If there is an error, our function's Promise will be rejected
  } finally {
    console.log('Hello, World!');
  }
}

theRightWay()
  .then(() => {})
  .catch(err => console.error(err));

Use Default Parameters

In JavaScript, we don’t have an easy way to ensure the functions we write will receive the right arguments. So it’s not uncommon to see something like this.


function doubleNumber(number) {
  if (number !== undefined) {
    return number * 2;
  }
  return 0;
}

console.log(doubleNumber(2)) // 4
console.log(doubleNumber()) // 0

Don’t know about you but I hate writing functions this way. What if I told you there is an easier way to handle corner cases like these.

With modern JavaScript, you can provide a default value to parameters in the function declaration.

You can write the previous example this way:


function doubleNumber(number=0) {
  return number * 2;
}

console.log(doubleNumber(2)) // 4
console.log(doubleNumber()) // 0

We are getting the same result but with less code. Now that’s an awesome feature.

Use The Right Tools For The Right Job

It’s no secret that JavaScript has the largest community of all programming languages. According to ModuleCounts there are 1070204 on NPM as of writing this article and today there have been around 835 new ones.

So it’s no surprise that JavaScript has some of the best tools that make the life of developer easier.

The ones I recommend you use:

Of course you don’t need to use these exact tools. You can use for example CoffeeScript or Flow instead of TypeScript or Rollup or Parcel instead of WebPack.

Do your own research to find the best tools that fit both the project and your preferences.

Like What You're Reading?

Powered by EmailOctopus

Use debugger

Did you know JavaScript has debugger built in itself. You can invoke it by placing debugger statement on the line where you want to pause the execution of code.

With that, you can now use Chrome DevTools or whatever DevTools you are using to see the current state of the whole application.

You can:

I could go on and on about debugger statement but why you should listen to me when Google has an awesome article about it. You check it out here.

There is more to console other than console.log

I don’t remember ever seeing someone using console API other than using console.log and it infuriates me.

To be fair, some methods from console API are not something you need in everyday situations but still, there are a ton of you should be using RIGHT NOW. Here are a couple of those methods that I am using almost every day.

console.assert

console.assert will take an assertion as it’s first argument and print an error message if that assertion is false. Simple as that but very useful.

console.time and console.timeEnd

Imagine having a really resource consuming function and you want to measure the time it needs to execute. Timers in Console API allow you to do that. Here is a quick example of how they work.

  console.time(); // We start our timer
  someComplexFunction(); //  We call our block of code
  console.timeEnd(); // We stop our default timer and print the result to console

Pretty cool, right? You can also label your timers so you can nest them.

  console.time('totalTime');
  console.time('complexFunctionTime');
  someComplexFunction();
  console.timeEnd('complexFunctionTime')
  /*
   * MORE CODE HERE 
  */
  console.timeEnd('totalTime');

Just remember you can have only 10,000 timers running simultaneously on one page.

console.error and console.warn

These are pretty straightforward. console.error is used the same way as console.log but will format it as an error. console.warn is the same but will format our message as a warning.

console.table

console.table is definitely the most mindblowing one. It allows us to log our objects in tabular format. Just imagine having an array with 100 objects in it and you have to log them out. It’s a nightmare to traverse. Luckily some very smart people came up with console.table.

const users = [
  {
    "company": "Ullrich - Nader",
    "username": "Lexus.Bailey3",
    "password": "Qi2zpcDdTouyQHE",
    "role": "user",
    "phone": "(555) 823-2264",
    "cell": "(972) 791-2009"
  },
  {
    "company": "Hilll, Homenick and Wyman",
    "username": "Enrico13",
    "password": "XUymoxm95hDO_Vj",
    "role": "user",
    "phone": "(115) 225-2681",
    "cell": "(214) 595-1386"
  },
  {
    "company": "Parisian Inc",
    "username": "Antwon.Koss",
    "password": "xGhGjlRbV6YXaPm",
    "role": "user",
    "phone": "(180) 871-7340",
    "cell": "(469) 245-6636"
  }
];

console.table(users);

If you try running this example in the browser you will most probably see something like this: Result of console.table

In conclusion

I hope you have found these tips useful. I tried to include something for everyone from beginner to expert.

To recap what we have learnt:

This is just the tip of the iceberg but I hope I have made the Web a little bit better by writing this.

Interested in learning more? Udacity has awesome course on front-end development. Check it out here.

Happy coding!

Like What You're Reading?

Powered by EmailOctopus