8 techniques to write cleaner JavaScript code
Tags: javascript, webdev, node, beginners
Published: 2022-01-03 01:27:01

I believe being a Software Engineer is just like being a Super Hero! And with great power, comes great responsibility. While writing code is an integral part of being a Software Engineer, just like estimations, brainstorming, writing unit tests are important aspects, writing clean code is really important as well. 
In this article, we're going to look at 8 different techniques to help you write cleaner JavaScript code.

> If you'd prefer watching a video instead, check this out:
[Video Tutorial](https://youtu.be/LTGcEIC2Tp8)

Now let's discuss each technique, one at a time.

## Pure Functions
A pure function is a function that always returns the same output, given the same input(s). It doesn't depend on any external variable apart from the inputs provided, nor it affects/changes any outside variable.
Having pure functions makes it a lot easier for testing as they make testing super easy as you can always stub/mock the inputs and test your expected values. Let's see the following example

```javascript
let name = "Peter Parker";
const splitName = () => {
  name = name.split(' ');
}
name = splitName();
console.log(name); // outputs [ 'Peter', 'Parker' ]
```

While the above code seems appropriate. It is not (lol). And that's because the `splitName` function depends on an outside variable named `name` and if someone else starts changing this variable, the function `splitName` starts providing a different output. Making it a non-pure function as we'd still be calling `splitName()` but the output is going to be different. 

Let's change this to a pure function and let's see how that would look like:

```javascript
let name = "Peter Parker";
const splitName = (nameString) => {
  return nameString.split(' ');
}
name = splitName(name);
console.log(name); // outputs [ 'Peter', 'Parker' ]
```

With the above change, the `splitName` is now a Pure Function because:
- It only relies on the input(s) (the `nameString` input).
- It doesn't change/re-assign any external variable

## Fewer or Named Parameters
When using functions, we often use positional parameters which have to be provided as they're declared with the function declaration. For example, in the call `arithmaticOp(num1, num2, operator)`, we can't provide the `operator` argument without providing `num1` and `num2`. And while this works for this example, for many functions, that'd become a problem.
Consider the following example:
```javascript
const createButton = (title, color, disabled, padding, margin, border, shadow)  => {
  console.log(title, color, disabled, padding, margin, border, shadow)
}
```
Looking at the above code, you can already see that in if we wanted to make any of the arguments optional (to use default values) while calling the `createButton` +, that'd be a disaster and might look something like this:
```javascript
createButton('John Wick', undefined /* optional color */, true ,'2px....', undefined  /* optional margin*/);
```

You can see that the above statement doesn't look **Clean** at all. Also, it is hard to see from the function-calling statement which parameter corresponds to which argument of the function. So this is a practice we could follow:
- If we have 2 or fewer arguments, we can keep them as positional arguments
- Else, we provide an object with key-value pairs

Let's use this technique with the above example and see how it looks like:
```javascript
const createButton = ({title, color, disabled, padding, margin, border, shadow})  => {
  console.log(title, color, disabled, padding, margin, border, shadow)
}

createButton({
  title: 'John Wick',
  disabled: true,
  shadow: '2px....'
});
```

Notice that the statement to call the `createButton` function is much cleaner now. And we can easily see which value in the key-value pair corresponds to the arguments for the functions. Yayy! 🎉

## Object / Array Destructuring
Consider the following javascript example in which we're taking some properties from an object and assigning to their individual variables:
```javascript
const user = {
  name: 'Muhammad Ahsan',
  email: 'hi@codewithahsan.dev',
  designation: 'Software Architect',
  loves: 'The Code With Ahsan Community'
}

const name = user.name;
const email = user.email;
const loves = user.loves;
```
In the above example, it is much cringe to use the `user.*` notation so many times. This is where Object Destructuring comes in. We can change the above example as follows with Object Destructuring:
```javascript
const user = {
  name: 'Muhammad Ahsan',
  email: 'hi@codewithahsan.dev',
  designation: 'Software Architect',
  loves: 'The Code With Ahsan Community'
}

const {name, email, loves} = user;
```
See! Much better. Right? Let's consider another example:
```javascript
const getDetails = () => {
  return ['Muhammad Ahsan', 'hi@codewithahsan.dev', 'Some Street', 'Some City', 'Some Zip', 'Some Country'];
}


const details = getDetails();
const uName = details[0];
const uEmail = details[1];
const uAddress = `${details[2]}, ${details[3]}, ${details[4]}, ${details[5]}`;
const uFirstName = uName.split(' ')[0];
const uLastName = uName.split(' ')[1];
```

Ugh. I even hated the code writing the above example 🤣. Had to do it though. You can see that the code looks super weird and is hard to read. We can use Array Destructuring to write it a bit cleaner as follows:

```javascript
const getDetails = () => {
  return ['Muhammad Ahsan', 'hi@codewithahsan.dev', 'Some Street', 'Some City', 'Some Zip', 'Some Country'];
}

const [uName, uEmail, ...uAddressArr] = getDetails();
const uAddress = uAddressArr.join(', ');
const [uFirstName, uLastName] = uName.split('');
console.log({
  uFirstName,
  uLastName,
  uEmail,
  uAddress
});

```
You can see how cleaner this is 🤩

## Avoid Hard-coded values
This is an issue that I often request changes for the Pull Requests I review. And is a no-go. Let's see an example:
```javascript
/**
 * Some huge code
 * 
 * 
 * 
 * 
 * 
 */

setInterval(() => {
  // do something
}, 86400000);
// WHAT IS THIS 86400000 ??? 🤔
```
Someone looking at the code would have no idea what this number stands for, how it was calculated and what's the business logic behind this. Instead of hardcoding this value, we could've created a constant as follows:
```javascript
const DAY_IN_MILLISECONDS = 3600 * 24 * 1000; // 86400000

setInterval(() => {
  // do something
}, DAY_IN_MILLISECONDS);
// now this makes sense
```

Let's consider another example:
```javascript
const createUser = (name, designation, type) => {
  console.log({name, designation, type});
}

createUser('Muhammad Ahsan', 'Software Architect', '1');
// WHAT IS this '1'? 🤔
```
Looking at the call for `createUser` method. It is really hard for someone reading the code to understand what this `'1'` stands for. I.e. what `type` of user this is. So instead of hard-coding the value `'1'` here, we could've created an Object mapping of the type of users we have as follows:
```javascript
const USER_TYPES = {
  REGULAR_EMPLOYEE: '1'
}

const createUser = (name, designation, type) => {
  console.log({name, designation, type});
}

createUser('Muhammad Ahsan', 'Software Architect', USER_TYPES.REGULAR_EMPLOYEE);
// smoooooooth 😎
```

## Avoid Short-hand variable names
Short-hand variables make sense where they're needed. Like if you've positional coordinates like `x` and `y`, that works. But if we create variables like `p`, `t`, `c` without having a context, it is really hard to read, trace and maintain such code. See this example for instance:
```javascript
const t = 25;

let users = ['Muhammad Ahsan', 'Darainn Mukarram'];

users = users.map((user) => {
  /**
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   */

  return {
    ...user,
    tax: user.salary * t / 100 // WHAT IS `t` again? 🤔
  }
})
```
The above examples shows that now the developer/reader has to scroll all the way up or go to the definition to try to understand what this variable is. Ergo NOT CLEAN CODE 😠. This is also called mind-mapping the variables in which only the author knows what they mean. So instead of the short hand variable name, we could've given this a proper name as follows:
```javascript
const taxFactor = 25;

let users = ['Muhammad Ahsan', 'Darainn Mukarram'];

users = users.map((user) => {
  // some code
  return {
    ...user,
    tax: user.salary * taxFactor / 100
  }
})
```
And now this makes much more sense.

## Set default Object values using Object.assign()
There might be cases where you'd want to create a new object from another object, providing some default value if the source object doesn't have them. Consider the following example:
```javascript
const createButton = ({title, color, disabled, padding})  => {
  const button = {};
  button.color = color || '#333';
  button.disabled = disabled || false;
  button.title = title || '';
  button.padding = padding || 0;
  return button;
}

const buttonConfig = {
  title: 'Click me!',
  disabled: true
}

const newButton = createButton(buttonConfig);
console.log('newButton', newButton)
``` 
Instead of doing all that, we can use `Object.assign()` to override the default properties if provided by the source object as follows:
```javascript
const createButton = (config)  => {
  return {
    ...{
      color: '#dcdcdc',
      disabled: false,
      title: '',
      padding: 0
    },
    ...config 
  };
}

const buttonConfig = {
  title: 'Click me!',
  disabled: true
}

const newButton = createButton(buttonConfig);
console.log('newButton', newButton)
```

## Use method chaining (especially for classes)
Method chaining is a technique that can be useful if we know the user of the class/object is going to use multiple functions together. You might have seen this with libraries like moment.js. Let's see an example:
```javascript
class Player {
  constructor (name, score, position) {
    this.position = position;
    this.score = score;
    this.name = name;
  }
  setName(name) {
    this.name = name;
  }
  setPosition(position) {
    this.position = position;
  }
  setScore(score) {
    this.score = score;
  }
}

const player = new Player();
player.setScore(0);
player.setName('Ahsan');
player..setPosition([2, 0]);
console.log(player)
```
In the above code, you can see that we needed to call a bunch of functions together for the player. If this is the case for your object/class, use method chaining. And all you need to do is to return the object's instance from the functions you want to chain. The above example can be modified as follows to achieve this:
```javascript
class Player {
  constructor (name, score, position) {
    this.position = position;
    this.score = score;
    this.name = name;
  }
  setName(name) {
    this.name = name;
    return this; // <-- THIS
  }
  setPosition(position) {
    this.position = position;
    return this; // <-- THIS
  }
  setScore(score) {
    this.score = score;
    return this; // <-- THIS
  }
}

const player = new Player();
player.setScore(0).setName('Ahsan').setPosition([2, 0]);
// SUPER COOL 😎
console.log(player)
```

## Use Promises over Callbacks
Promises have made our lives easier. We had something called callback hell a couple of years ago that made the code so hard to read. It looks something like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ssr9izx4571fazh655ej.png)

Even if I'm working with a library that has callbacks, I try to add a wrapper there that promisifies that (yeah, that's a term now). Let's consider the following example:
```javascript
 const getSocials = (callback) => {
  setTimeout(() => {
      callback({socials: {youtube: 'youtube.com/CodeWithAhsan', twitter: '@codewith_ahsan'}});
    }, 1500);
}

const getBooks = (callback) => {
  setTimeout(() => {
    callback({books: ['Angular Cookbook']});
  }, 1500);
}

const getDesignation = (callback) => {
  setTimeout(() => {
    callback({designation: 'Software Architect'});
  }, 1500);
}

const getUser = (callback) => {
  setTimeout(() => {
    callback({user: 'Ahsan'});
  }, 1500);
}

 getUser(({user}) => {
    console.log('user retrieved', user)
    getDesignation(({designation}) => {
      console.log('designation retrieved', designation)
      getBooks(({books}) => {
        console.log('books retrieved', books)
        getSocials(({socials}) => {
          console.log('socials retrieved', socials)
        })
      })
    })
  })
```
All of the functions in the above code are asynchronous and they send back the data after 1.5 seconds. Now if there were 15 different functions involved, imagine what it would look like. Probably like the image I shared above 😅. Instead of having this callback hell, we can promisify our functions and use promises as follows for better readability:
```javascript
const getSocials = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({socials: {youtube: 'youtube.com/CodeWithAhsan', twitter: '@codewith_ahsan'}});
    }, 1500);
  })
  
}

const getBooks = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({books: ['Angular Cookbook']});
    }, 1500);
  })
}

const getDesignation = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({designation: 'Software Architect'});
    }, 1500);
  })
}

const getUser = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({user: 'Ahsan'});
    }, 1500);
  })
}

  getUser()
    .then(({user}) => {
      console.log('user retrieved', user);
      return getDesignation();
    })
    .then(({designation}) => {
      console.log('designation retrieved', designation)
      return getBooks();
    })
    .then(({books}) => {
      console.log('books retrieved', books);
      return getSocials();
    })
    .then(({socials}) => {
      console.log('socials retrieved', socials)
    })
```
 You can see that the code already is much readable now as all the `.then()` statements are indented and show what data is retrieved in each `.then()` step. We can easily see the steps using this syntax as every `.then()` call returns the next function call along with its promise. 

Now we can take it up a notch and make our code even more readable. How? By using `async await`. We'll modify our code as follows to achieve that:
```javascript`
const getSocials = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({socials: {youtube: 'youtube.com/CodeWithAhsan', twitter: '@codewith_ahsan'}});
    }, 1500);
  })
  
}

const getBooks = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({books: ['Angular Cookbook']});
    }, 1500);
  })
}

const getDesignation = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({designation: 'Software Architect'});
    }, 1500);
  })
}

const getUser = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({user: 'Ahsan'});
    }, 1500);
  })
}

const performTasks = async () => {
  const {user} = await getUser();
  console.log('user retrieved', user);

  const {designation} = await getDesignation();
  console.log('designation retrieved', designation);

  const {books} = await getBooks();
  console.log('books retrieved', books);
  
  const {socials} = await getSocials();
  console.log('socials retrieved', socials);
}
```

Notice that we wrapped our code inside the `performTasks()` function which is an `async` function as you can see the usage of the `async` keyword. And inside, we're making each function call using the `await` keyword which essentially would wait for the promise from the function to be resolved before executing the next line of code. And with this syntax, our code look as if it was all synchronous, however being asynchronous. And our code is a lot more cleaner 🙂

# Conclusion
I hope you enjoyed reading the article. If you did, make sure to hit like and bookmark. And check my [YouTube Channel](https://youtube.com/CodeWithAhsan) for more amazing content. And if you feel adventurous and are interested in taking your #Angular skills to the next level, check out my [Angular Cookbook](https://ahsanayaz.com/ng-book)