10 best JavaScript practices recommended by Top Developers

10 best JavaScript practices recommended by Top Developers

Featured on daily.dev

If you have recently started learning JavaScript or a beginner I would highly recommend you to read the complete article so that you follow better practises while coding in JavaScript as it's a little different.

It's been a few months since I have started programming in JavaScript and these are some of the practises I and several other senior developers follow.

Don't use ' == '

In JavaScript there are both '== ' and '===' and you might be used to "==" in other programming languages and might continue using the same, But using it without knowing how it works might bring few bugs which might be difficult to track. Read my previous article to know more.

Use "let" over "var"

When declaring variables you might have come across 'var' and 'let' but "var" is not recommended as it brings some problems along with it. It will be easier to understand with an example.

for(var i=0;i<5;i++){
    console.log(i);
}
console.log(i)

// Output : 0 1 2 3 4 5

Notice that variable i is still accessible outside the block/for-loop which is bad. On the other hand let's write the same code using "let"

for(let i=0;i<5;i++){
            console.log(i);
        }
        console.log(i)
// Output : i is not defined

the output throws an error as the variable i is not accessible outside the for loop/block thanks to "let" as its block-scoped.

There is also something called as shadowing. For example

var a= 100;
function example(){
a=20;
} 
example();
// 20 just shadowed 100 of the global scope 
console.log(a);
// Output : 20

Here you can see that both the variables access the same memory space that's why changing the value inside a block changed the value of the variable outside.

'var' also leads to Hoisting which I will discuss later in the same article. Whenever you want to declare variables in the future where data might change use"let".

Go through some of the articles regarding the same from Caelin Sutch - Let vs var and Faith Gaiciumia - Difference between var and let .

Use Immutable variables or "const"

Use 'const' to declare variables as much as you can Unless the data changes in the variable like the 'i' inside the for-loop. Here are few examples


const x = 'SECRET'
console.log(x)
//Output: SECRET
// Here are few illegal things 
// number 1

const x = 'SECRET'
x='Hacking'
console.log(x)
// Error: Identifier 'x' has already been declared

// Number 2 
const x;
x="SECRET";
// Error: Missing initializer in const declaration

Read more about const here .

Use Function Expressions

With the introduction to ES6, there is a new way to write functions. Here are few examples

// Method 1  
let sum = function(a, b) {
    return a + b;
  };
//Method 2 
let sum = (a, b) => { // using arrow function
    return a + b;
  };
// Method 3 
let sum= (a,b) => a+ b ;  /* if there is only line inside function. 
There is no need for return */
//Method 4 (Annonymous Function)
(() =>{
    console.log("annonymous  functions")
  })();

// Notice our function doesn't have anything but still works.

This is called Function Expression. Having function expression instead of the normal function declaration also avoids Hoisting.

What is hoisting?

to explain hoisting in functions let me take an example.

exampleFunction(); // function call 

function exampleFunction(){ // function declaration

console.log("Hashnode is awesome")

}
// Output: Hashnode is awesome

Did you observe how calling exampleFunction() even before definition didn't throw any error.

Why did this happen ? In JavaScript unlike other languages an entire copy of function is copied in the execution context even before executing the code. But doing the same using function expressions would throw an error.

To understand that let's see what happens to variables declared using "var" keyword. Here the memory is allocated even before the execution of the program but it will have undefined value until it goes to the line where the variable is assigned with a value.

console.log("Before Assignment: ",a);

var a=10;

console.log("After assignment: ",a)

/* Output:

        Before Assignment: undefined

        After assignment: 10

The above is called variable hoisting. But coming back to function expressions.

exampleFunction();

const exampleFunction= () =>{

console.log("Hashnode is awesome");

}

// Output : ReferenceError: exampleFunction is not defined

In function expressions the values are not copied to memory even before execution which leads to an error.

Some developers leverage hoisting with function definition by declaring all the functions in the bottom and access it on top but it depends on you. I would suggest you use function expressions and call functions after declarations as it might lead to fewer errors and even reading becomes easier.

Use Pure Functions

Pure functions is a function where everytime you call that function you get the same result and it should not have side effects.

Why should you care ?

This helps in testing the function and debugging becomes really easy. Understanding this wih an example would be better . Below is an impure function which multiples all values in an array by 2.

array=[1,2,3,4,5];
const doubleArrayValuesImpure= (array)=>{

    for (const number in array) { 

   array[number] = array[number] *2 ; 

    }
    return array; 
    }
// changes the values in original array

Let's call the same function 2-3 times

console.log(doubleArrayValuesImpure(array)) ;
console.log(doubleArrayValuesImpure(array)) ;
console.log(doubleArrayValuesImpure(array)) ;

/*Output:
[ 2, 4, 6, 8, 10 ]
[ 4, 8, 12, 16, 20 ]
[ 8, 16, 24, 32, 40 ]
*/

Notice how the values are changing on the original array every time we call the function. Let's write a pure function with the same logic.

const doubleArrayValuesPure= () => array.map(number => number *2) ; 
// returns a new array with new values

calling the pure function

console.log("Pure Function results")
console.log(doubleArrayValuesPure(array));
console.log(doubleArrayValuesPure(array));
console.log(doubleArrayValuesPure(array));
/*Output:
[ 2, 4, 6, 8, 10 ]
[ 2, 4, 6, 8, 10 ]
[ 2, 4, 6, 8, 10 ]
*/

Notice how calling the same function 3 times didn't change the original array. Whenever we declare functions we should focus on writing pure functions unless we are changing a file or writing into database.

Name things properly

We developers are very lazy and usually use random names for variables and cry in a corner when we have to revisit the same code. You don't actually need to document your code if you have named things in the right way. Also use camel casing.

// BAD
let ad="124 Broadkill Rd #483";
let c="Milton";

// Good
let address="124 Broadkill Rd #483";
let city="Milton";

Use Destructing

Let's consider having an object called person.

const person= {
    name: "Hrithwik",
    age:20,
    number: 9999999999,
    jobStatus:"Give me a job please",
}

Now if you want to access multiple values which are inside an object you use object destruction instead of manually writing extra lines of code. Check this out.

//noob 
let name = person.name;
let age= person.age;
let phoneNumber= person.number;
let occupation= person.jobStatus


// Pro 
let { name,age,number, jobStatus} = person ;

But do note that the name of the variable outside the object should be the same as the ones inside. You can also destructuring arrays, read this article to know more.

Use Promises over Call Backs

I am not sure if you have discovered about handling asynchronous code. if you haven't skip this and read about it later.

Here is an example of asynchronous code which takes the user object and checks the commit name in the first repository of the GitHub user.

console.log("before");

getUser(1,(user)=>{
    console.log("username:",user.github);
    getRepo(user.github,(repo)=>{
        console.log("Repositories are " + repo);
        getCommits(repo[0],(commit)=>{
            console.log("Commits")
        })
    })
})


function getUser(id,callback   ){
    setTimeout(()=>{
        console.log("reading user from database")
    callback({ id:id*2,github:'hrithwikbharadwaj'});
},2000)
}

function getRepo(username,callback){
    setTimeout(()=>{
        console.log("reading repo from "+username);
        callback(['repo1','repo2','repo3']);
    },2000)

}
function getCommits(repo,callback){
    setTimeout(()=>{
        callback(repo);
    },300);
}

console.log("After")

Callbacks are cool unless there are a lot of callbacks inside callbacks.

Isn't it difficult to read? It's also called callback hell/Christmas tree problem. You can fix it by named functions but a better way to do it is by using promises. Here is the same code using promises.

console.log("before")

getUser(1)
.then(user=> getRepos(user.github)) // is user available ?
.then(repos=>getCommits(repos[0])) // oh great now find his first repo
.then(commits=>console.log("commit name: ",commits)); // found the repo ? cool now show the commits



function getUser(id){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
        console.log("reading user from database")
    resolve({ id:id*2,github:'hrithwikbharadwaj'});
},2000);
    })

}

function getRepos(username){
     return new Promise((resolve,reject)=>{
          setTimeout(()=>{
        console.log("reading repo from "+username);
        resolve(['repo1','repo2','repo3']);
    },2000);
     });


}
function getCommits(repo){
   return new Promise((resolve,reject)=>{
        setTimeout(()=>{
        resolve(['commits']);
    },2000);
   });
}

console.log("After")

Read more about promises by Jome Favourite here on Hashnode.

Write Functions for everything

Most of the times we write functionality for many things but we don't name it. Keeping everything inside a function helps to again search for what you want easily. Also just keep one functionality inside one function.

// Bad code
let number1=2;
let number2=5;

let sum= number1+number2;

// Good
const sum = (number1,number2) =>{
    return number1+number2;
}

console.log(sum(2,3))

Eliminate Decimals without killing performance

You might have a situation where you need to generate random whole numbers between 0 to 1000 and we usually use math.floor or math.ceil

Math.floor(Math.random()*1000)

but instead of that try using

~~ (Math.random()*1000)

this can improve performance when micro-optimization of code. refer to this article to know more.

Deleting array like a pro

There are several ways to delete an array on javascript but this is my favourite way.

let numbers=[1,2,3,4];
numbers.length=0;

Most of the times we jump into a language without knowing its fundamentals but I hope with this article you would write better code in JavaScript .

You can never learn from just one article. Google concepts and read multiple articles, books and play around the code by making projects and that's how you learn.

Checkout a curration of learning resources for JavaScript by Rahul here .