DEV Community

Cover image for Some Best Practices of Javascript for clean and better code quality...
Vinay Dagar
Vinay Dagar

Posted on

Some Best Practices of Javascript for clean and better code quality...

JavaScript is a very popular and widely used programming language. Initially it was developed only as a scripting language, but now it is doing way more than that. It's community is growing so is the Javascript.

As the new features is being added frequenty, it is hard to write optimized and clean code, mostly happens when you are a beginner.


Today I'm going to show you some of the best practice that you can follow to write optimized and clean code.

So with out any further due, let's get started

  1. Chain array methods Yes, you read it correct, the thing that we use more often is Array Prototype methods like, map, filter, find, reduce

basic scenario will be mapping through the filtered list, instead of storing the filter result in a varibale and then mapping on, we can directly chain the methods.

const userList = [
  {
    name: 'Jhon Doe',
    age: 26,
    occupation: 'Software Engineer',
  },
  {
    name: 'Kapil',
    age: 19,
    occupation: 'Student',
  },
  {
    name: 'Matt',
    age: 32,
    occupation: 'Software Architect',
  },
];


const mappedUsers = userList.filter(user => user.age > 26)
                    .map(user => ({ isVerified: true, ...user}))
                    .reduce(...)

Enter fullscreen mode Exit fullscreen mode

2.
Logical assignment
There might be hte scenario where we need to assign something when any given variable is null or undefined, normal way using if would be like this:

let userProfile;
let value = {
   name: 'someValue',
};
if(userProfile === null || userProfile === undefined) {
   userProfile = value
}

 //   OR
if(!userProfile) {
   userProfile = value
}
Enter fullscreen mode Exit fullscreen mode

we can simply do assign using logical or nullish collision operator, like this:

userProfile ??= value;

// OR

userProfile &&= value
Enter fullscreen mode Exit fullscreen mode

3.
Parallel API calls
While building any Application, mostly in frontend, sometimes we might need to call several API simultaneously, if the APIs are not dependent on each other, we can send a paraller request using Promise in Javascript

const getData = async () => {
  try {
    const first = await fetch();
    const second = await fetch();
    const third = await fetch();
    const fourth = await fetch();
  } catch (err) {
    // ...
  }
}
Enter fullscreen mode Exit fullscreen mode

Instead of this we can send the parllel call,

const getData = async () => {
  try {
    const [ first, second, thrird, fourth] = await Promise.all([
      fetch(),
      fetch(),
      fetch(),
      fetch(),
    ])
  } catch (err) {
    // ...
  }
}
Enter fullscreen mode Exit fullscreen mode

we can also use Promise.allSettled() instead of Promise.all() based

4.
Using Objects instead of Switch for event binding
In most of the applications that we develop, there are some scenario where we need to handle events, and mostly we do that using switch statement or if...else,

const handleEvent = (event) => {
  switch(event) {
    case 'success': {
      // handleSuccess
    }
    case 'error': {
      // handle error
    }
    case 'pending': {
      // handle pending 
    }
    default:  {
      // handle default
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

instead of doing this, we can simply create the object with event as key and function as its value something like this:

const eventHandler = {
  success: handleSuccess,
  error: handleError,
  pending: handlePending
}

const handleEvent = (event) => {
  const handler = eventHandler[event] ?? handleDefault;
  handler();
}
Enter fullscreen mode Exit fullscreen mode

here we need to use bracket notation to call the appropriate functions out of object.

5.
Doing one thing in a function
So, the basic behaviour of a function is to perform a particular task, and whatever we write in it, will be executed.

making a single function perform every thing will make the code hard to read, lenghty, and hard to debug.
Dividing the single functionality inside a function and calling the rest one after or inside the other fucntion, will help for better understanding of code and make our code easy to debug

the best scenario for this will be user registeration

const signupUser = () => {

  // checking for existing user

  // password encryption

  // creting new user
}
Enter fullscreen mode Exit fullscreen mode

so doing all of this stuff in one function make it more clumsy, what we can do instead is

const signupUser = () => {

  const isUserAlreayExist = checkExistingUser(/* username */);

  // handle if user already Exist 

  const hashedPAssword = encryptPAssword(salt, password);

  createUserAndReturn(/* user data */)
}
Enter fullscreen mode Exit fullscreen mode

6.
Using console.trace()
To check some result and or sometime to debug small things, we use console.log() right?

which just give us the message that we wrote.

but some times in bigger applications, we can have log statements, and keeping track of the log statement, which log represents which part might get a little hard, so to prevent that we can use console.trace()

The trace() method displays a trace that show how the code ended up at a certain point.

It returns some additional information, apart from the message that we wrote, the information includes from where this statement has been logged, from which function and line number.

7.
Using Logpoint in vscode

While debugging the application we add breakpoints to stop the execution of the program at a certain point, but sometime we just wnat to see if the particular code got executed or not, for for that we can add Logpoint

the result will show the output in the console as it has been logged while going through that point, in this way we don't event have to worry about the console statements in production.

We can add the logpoint by right clicking the line number in the vscode
vscode logpoint image


Some honourable mentions

  • Use async / await instead of promise callback chain
  • Use Typescript for larger applications.
  • Use comments in the code where ever necessary.
  • Use destructing instead of chaining object or using indexe in array
  • Use less third part library (only use when necessary).
  • Read...

Conclusion

These are some of the best practices that I follow to make my code clean, neat, readable and easy to debug. The key for clean code is an ongoing journey as the Javascript is contionusly evolving language.
I hope this might help you in your coding journey.

Happy coding!

Discussion (9)

Collapse
lukeshiru profile image
Luke Shiru

For the Parallel API Calls you don't even need async/await, you can just:

const getData = () =>
    Promise.all([fetch(), fetch(), fetch(), fetch()])
        .then(([first, second, third, fourth]) => {
            /* ... */
        })
        .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

For the Using Objects instead of Switch for event binding you forgot about the default handler, you can use ?? for it:

const eventHandler = {
    success: handleSuccess,
    error: handleError,
    pending: handlePending,
};

const handleEvent = event => eventHandler[event] ?? handleDefault;
Enter fullscreen mode Exit fullscreen mode

Also in the honorable mentions "Use async/await instead of promise callback chain". Why tho? If you do things correctly, the code stays clean:

try {
    const response = await fetch(ENDPOINT);
    const data = await response.json();
    console.log(data);
} catch (error) {
    console.error(error);
}

// vs

fetch(ENDPOINT)
    .then(response => response.json())
    .then(console.log)
    .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

I'm not saying async/await is not useful, I'm just saying that you don't need them always, and if you do things correctly, your code stays simple without that syntax sugar.

Cheers!

Collapse
lexlohr profile image
Alex Lohr

The last point is heavily disputed and there are a lot of opinionated people on either side.

My position is: use what fits your case best. You can also mix await promise emitter().catch(...) for extra conciseness.

Collapse
jonrandy profile image
Jon Randy • Edited on

Might want to check number 4. The sample code is not correct.

Also, you might want to consider switching on syntax highlighting in the code samples. You can do this by adding js immediately after the opening backticks of the code block

Collapse
_vinay_dagar profile image
Vinay Dagar Author • Edited on

Thanks @jonrandy

Collapse
fhefh2015 profile image
fhefh2015 • Edited on

Logical assignment

There might be hte scenario where we need to assign something when any given variable is null or undefined, normal way using if would be like this:

Logical assignment

let userProfile;
let value = {
   name: 'someValue',
};
if(userProfile === null || userProfile === undefined) {
   userProfile = value
}

 //   OR
if(!userProfile) {
   userProfile = value
}


userProfile ??= value;

// OR

userProfile &&= value
Enter fullscreen mode Exit fullscreen mode

It's much easier to write, but the readability is so bad.

Collapse
valeriavg profile image
Valeria

Some of these can be dangerous practices.

Chaining too much array methods, for instance, will make it significantly harder to debug if any problem appears, not to mention all the extra allocations.

You shouldn't have to fetch several requests at the same time, it slows them down and creates extra load on the server. Promise.all and similar are a workaround for the cases when you don't have any control over the server but still need to make sure that all data is delivered.

There's nothing wrong with using switch or chaining promises, or using a for loop instead of a functional style and mutating the array directly when that makes sense.

JavaScript is one of the most versatile languages and the only best practice is has is:

Keep things as simple as possible to not shoot yourself in the feet

Collapse
rishit profile image
Rishit Bansal • Edited on

Nice Post! One thing I would like to point out, Parallel API calls can be detrimental sometimes if you go overboard with them. Say my function did 5 API calls simultaneously, and this function was called at a high rate in my code you can actually end up causing a lot of load on the target server. Moreover, most APIs also have rate limiting setup and successive requests might be blocked.

A way to circumvent this is to use a Queue of promises and process promises from the queue at a fixed concurrency level (say 2 at a atime, or 3 at a time). P-queue is a great library to achieve this.

Collapse
rezi profile image
Tomas Rezac
  1. It is not called nullish collision but nullish coalescing assignment. And this userProfile &&= value does exact opposite than you described. If userProfile has falsy value, then it keeps it, if not, then the value is overwritten by value.
Collapse
buondevid profile image
buondevid

It's not destructing but destructuring...