Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Functions

JavaScript is an easy to learn programming language. It’s easy to write programs that runs and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at how to write JavaScript functions that are more robust and maintainable.

Multiple Parameters is Better Than One Object Parameter

If there aren’t too many parameters in our function, we should have multiple parameters instead of one single object parameter.

It’s much clearer to have multiple parameters instead of one single object parameter.

For instance, the following:

const fullName = (person) => `${this.firstName} ${this.lastName}`

is harder to read than:

const fullName = (firstName, lastName) => `${firstName} ${lastName}`

because we don’t know what the person parameter in the function signature contains until we look at the code.

In the second example, we know that firstName and lastName are the parameters right away.

If the functions are more complex than what we have above, then it’s even harder to trace what our function has.

A good rule of thumb is that for functions that have 5 or fewer parameters, then they should be listed separately in the function signature.

Otherwise, we have no choice but to combine some or all of them into one object parameter to keep the number of parameters 5 or less.

This is because once a function has more than 5 parameters, now the function signature becomes harder to read.

Also, if a function have lots of arguments, then it’s harder to remember the order to pass in the arguments so we can call our function correctly.

Skipping parameters is also a problem if our function has lots of parameters since we have to check where to pass in undefined to so that we can skip passing in a value for that parameter.

If we pass in an object, then we can just set properties to undefined and be done with calling the function instead of thinking where we need to pass in undefined if our function has lots of parameters.

Destructuring and Functions

Destructuring is a nice feature of ES2015 that lets us decompose array entries and object entries into their own variables.

In terms of functions, we can use it to destructure object or array parameters to selectively reference object properties or array entries that we need.

For instance, if we have the following function:

const fullName = (person) => `${this.firstName} ${this.lastName}`

Then we can rewrite it with the destructuring syntax by writing:

const fullName = ({
  firstName,
  lastName
}) => `${firstName} ${lastName}`

And then we can call it as follows:

fullName({
  firstName: 'joe',
  lastName: 'smith'
})

In the code above, we passed in:

{
  firstName: 'joe',
  lastName: 'smith'
}

to fullName , and then because of the destructuring syntax, the JavaScript interpreter will automatically get the value with the given property name and interpolate the string with that variable value.

So firstName in the destructured object is the same one that’s referenced in the string, which is also the same as the firstName property that’s passed into the fullName function.

Therefore, the console log output should be 'joe smith' .

It also works with nested objects. For instance, we can write:

const fullName = ({
  name: {
    firstName,
    lastName
  }
}) => `${firstName} ${lastName}`

Then if we call fullName as follows:

fullName({
  name: {
    firstName: 'joe',
    lastName: 'smith'
  }
})

We’ll get the same result as before. It’s important to note that we don’t have to include every property in the object in the destructuring syntax.

We can also write:

const name = ({
  name: {
    firstName,
  }
}) => `${firstName}`

and call it like:

name({
  name: {
    firstName: 'joe',
    lastName: 'smith'
  }
})

Destructuring also works for arrays. In terms of functions, we can use it to destructure array parameters into variables. For instance, we can write:

const fullName = ([firstName, lastName]) => `${firstName} ${lastName}`

Now the fullName function takes an array as the argument instead of an object.

Then we can call it as follows:

fullName(['joe', 'smith'])

And we’ll get 'joe smith' as the returned value as we did before.

Conclusion

If our function takes 5 or fewer parameters, then they should be separate for easy readability and reduce the cognitive load of whoever’s using the function.

Also, the destructuring syntax is great for situations when we need to take an object or array entries as parameters in our function and we want to selectively reference the entries from those arguments.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Code Organization

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at how to organize our JavaScript code that it’s clean and harder to break.

Keep Functions Simple

Functions are all pieces of code that can be invoked repeatedly with different arguments.

They should be as simple as possible so that they’re easy to test and maintain. Keeping them simple also makes it easier to understand so that everyone can use it or update it if required.

For instance, an example of a simple function is something like the following:

const add = (a, b) => a + b;

The function above is simple. It only does one thing, which is adding 2 numbers together. It’s simple since it only does one operation and returns it.

Also, testing is easy since we only have to test one operation. It’s also easy for us to understand the code.

Divide Code Into Modules

Since ES2015, JavaScript modules is a standard feature of JavaScript. They allow us to divide code into small chunks and expose what’s required. They load asynchronously, which means no tying up the main thread, unlike scripts that are loaded with script tags, which loads synchronously.

Also, the code in functions is hidden from the outside unless we explicitly export the members of a module. This is something that old-style scripts can’t do.

In old-styles scripts, all top-level code is exposed to the outside, which isn’t ideal and it leads to the use of hacky solutions like immediately invoked function expressions (IIFEs) to be used to hide code that we don’t want to expose to the outside.

Also, modules have strict mode turned on by default, so that it’s very hard for us to make mistakes that we would otherwise make if it’s off, like assigning a read-only global variable or value to a different value or declaring global variables accidentally.

Now that we have modules, we don’t have to worry about how to organize our code and hide code from the outside. All we have to do is to export the code that we want to the outside world with the export keyword.

If we want to use code from another module, then we can import it with the import keyword.

All imports are read-only versions of the member that’s exported from the originating module. Also, static checks are done to stop us from importing things that don’t exist, which is even better.

Old-style scripts don’t provide any static checks for anything from the outside, so it’s easy for us to make mistakes and make typos in our code, resulting in errors.

This is much better than using script tags since it lets us isolate our code much more easily and provides static checks so that it’s hard for us to make mistakes, creating more robust code in the process.

The common use cases for modules is simple. All we have to do is create JavaScript modules files and then use the export and import keywords as follows:

module.js

const foo = 1;
export const bar = 2;

index.js

import { bar } from "./module";

console.log(bar);

In the code above, we create a module by creating module.js and then adding an export expression to export our bar constant.

Since we don’t have export in front of the first line, we didn’t export foo , so it won’t be available to the outside.

Then we imported bar in index.js and used it in the console log. If we try to import foo , we’ll get an error because the static analysis will indicate that foo wasn’t exported, so we can’t import it.

We can also do the import on the fly with the import function. It returns a promise so it’s also asynchronous.

It allows us to import members from another module dynamically. For instance, in index.js , we can write the following:

(async () => {
  const { bar } = await import("./module");
  console.log(bar);
})();

In the code above, we have the import function call, which imports module.js as we did before, but we used await get resolved value, which is an object including bar .

Then we referenced it in console.log as we did before.

Conclusion

Keeping functions simple is a good idea if we want to write robust code because it’s easier to read, understand, and test.

JavaScript modules should be used as much as possible to keep code private while exposing others. Only exported code is exposed to the outside.

Also, strict mode is enabled by default in modules. Static analysis is also available for modules to prevent us from making mistakes when referencing code.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Async Code

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at the best practices for writing asynchronous code with Javascript.

Use Promises

Promises are very useful for writing asynchronous code in JavaScript since we can chain multiple pieces of asynchronous code together without nesting multiple asynchronous callbacks.

We can’t nest too many asynchronous callbacks deeply because it’s very hard to read and follow. Therefore, it’s very easy to create bugs and making changes will be very slow.

With promises, we can chain multiple promises together and make writing asynchronous code in a series very easy.

For instance, we can write the following code to chain multiple promises together as follows:

Promise.resolve(1)
  .then(val => Promise.resolve(val * 2))
  .then(val => Promise.resolve(val * 3))

In the code above, we called Promise.resolve , which is asynchronous and returns a promise. Each Promise.resolve call is queued at the end of the event loop and runs when the JavaScript main execution thread is free of any queued tasks.

The chaining we did above is much cleaner than we would have with nested callbacks. The val just takes the value we have from the promise that’s resolved before.

As we can see, we can also take the value from the previously resolved promise and do something with it. This is something that’s hard to do with nested callbacks as we have to do 3 levels of nesting to do the same thing.

To catch errors that occurs when any promise is being executed, we can call catch with a callback as follows:

Promise.resolve(1)
  .then(val => Promise.resolve(val * 2))
  .then(val => Promise.reject('fail'))
  .catch(err => console.log(err))

In the code above, we called Promise.reject , which will cause the promise chain to fail. Promise.reject takes a reason for the failure and the reason will be available in the err object in the catch callback.

So, we’ll see 'fail' logged from the promise callback.

The promise stops running when the first promise that fails is run, so we can only have one catch call in the promise chain.

The error handling is also something that’s not available with nested async callbacks unless the code that takes the callback explicitly returns an error as a parameter in the callback.

Also, we have to do more nesting to handle errors in nested async callbacks, which means that it makes reading the code even harder and create more bugs.

To make promises even shorter, we can use the async and await syntax for chaining promises.

For instance, we can write the following code to use async and await so that we can remove the then and callbacks but do the same thing as before:

(async () => {
  const val1 = await Promise.resolve(1);
  const val2 = await Promise.resolve(val1 * 2);
  const val3 = await Promise.resolve(val2 * 3);
})()

This is cleaner than using then and callbacks since we now have no callbacks in our code and it’s only 3 lines. Also, the readability of the code isn’t made worse by using it.

val1 , val2 , and val3 have the same values the parameter in the then callbacks, which are the resolved values.

To catch errors when a promise in the promise chain fails, we can just use try...catch as we do with synchronous code.

For instance, we can write the following code to catch promises with async and await :

(async () => {
  try {
    const val1 = await Promise.resolve(1);
    const val2 = await Promise.resolve(val1 * 2);
    await Promise.reject('fail')
  } catch (err) {
    console.log(err)
  }
})()

In the code above, we wrapped a try...catch block around our promise code so that we can catch errors with our promise chain. It stops running when the promise fails so one catch block will do everything.

Photo by Hello I’m Nik ? on Unsplash

Run Unrelated Promises in Parallel with Promise.all

We should use Promise.all to run promises that are unrelated in parallel. This way, we won’t have to wait for one promise to resolve before running another unnecessarily, thereby speeding up our code.

Promise.all takes an array of promise and returns a promise that has the array of resolved values.

For instance, we can write the following code to use Promise.all :

Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ])
  .then(([val1, val2, val3]) => {
    console.log(val1, val2, val3);
  })

In the code above, we have 3 promises with Promise.resolve in an array. We then use that to call Promise.all to resolve them in parallel. Then we get the resolved values in the callback and log them.

With the async and await syntax, we can write:

(async () => {
  const [val1, val2, val3] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ])
  console.log(val1, val2, val3);
})();

since Promise.all returns a promise.

Conclusion

When writing async code in JavaScript, we should be using promises. This way, they can be chained easily and we can also run them in parallel with Promise.all .

Removing nested increases readability by a lot and so mistakes are less likely to be made. Also, error handling is standard since we can call catch with promises to catch errors.

The async and await syntax takes chaining promises shorter. Also, we can catch errors with it with try...catch .

Categories
JavaScript Best Practices

JavaScript Best Practices — Comparison and Old Constructs

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at some best practices when writing JavaScript code, including comparisons, accessors, objects, and loops.

=== and !== are Better Than == and !=

=== and !== saves us a lot of trouble from JavaScript type coercion. The algorithm for comparison using == and != because of it.

With == we get results like:

  • [] == false
  • [] == ![]
  • 2 == "02"

all of which return true .

There’s a lot list of rules that we have to aware of listed at https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3 for == and != .

Therefore to avoid remembering all that and avoid weird results like the ones that are listed above, we should use === and !== for comparison.

So, we should avoid writing the following code:

if (x == 1) {

}

and instead, write:

if (x === 1) {

}

Other examples of good code include:

a === b
foo === true
apple !== 1
value === undefined
typeof foo === 'undefined'
'foo' !== 'bar'

Grouping Accessor Pairs in Object Literals and Classes

Grouping getters and setters for the same field together makes sense. It’s easier to read and we won’t have to search for them all over the code.

For instance, instead of writing:

const obj = {
  get a() {
    return this.val;
  },

  b: 1,

  set a(val) {
    this.val = val;
  }
}

and:

class Foo {
  get a() {
    return this.val;
  }

  b() {
    return 1;
  }

  set a(val) {
    this.val = val;
  }
}

We should instead write:

const obj = {
  get a() {
    return this.val;
  },

  set a(val) {
    this.val = val;
  },

  b: 1,
}

and:

class Foo {
  get a() {
    return this.val;
  }

  set a(val) {
    this.val = val;
  }

  b() {
    return 1;
  }
}

Checking for Own Properties in for…in Loops

for...in loop loops through an object’s own properties and also the properties up the prototype chain.

This will lead to unexpected items being looped through within the loop.

To avoid this, we should add a check to see if we’re looping through an object’s own properties so that we don’t accidentally loop through an object’s prototype’s properties.

Therefore, instead of writing:

for (const key in foo) {
  console.log(foo[key]);
}

We should write the following code instead:

for (const key in foo) {
  if (Object.prototype.hasOwnProperty.call(foo, key)) {
    console.log(foo[key]);
  }
}

The following code is also good:

for (const key in foo) {
  if ({}.hasOwnProperty.call(foo, key)) {
    console.log(foo[key]);
  }
}

In both examples, we called hasOwnPropterty with the call method to change the this value in hasOwnProperty to foo and pass in the key as the first argument to hasOwnProperty so that we can check if the key is in foo itself.

Don’t Have Too Many Classes Per File

Having too many classes per file makes it harder and read and navigate. The structure is also worse.

It’s best to limit each code file to a single responsibility. For instance, instead of writing in one file:

class Foo {}
class Bar {}

We should split them into 2 files:

foo.js :

class Foo {}

bar.js :

class Bar {}

Photo by Syed Ahmad on Unsplash

Disallow Use of Alert

JavaScript’s alert , confirm , and prompt functions are obtrusive as UI elements and should be replaced with modals, dialogs, or something else that’s less obtrusive.

It’s also often used for debugging code. Therefore, we should avoid using them unless we’re actually using them for alerting users and asking questions.

Even then, they should be used sparingly.

Stop Using arguments.caller and arguments.callee

arguments.caller and arguments.callee makes some code optimizations impossible. They’re also forbidden in strict mode.

They also don’t work in arrow functions since they don’t bind to arguments .

Therefore, we should either use arrow functions if we don’t need to bind to this . If we need to use traditional functions, then we shouldn’t reference those 2 properties in our function.

So:

function foo() {
  var callee = arguments.callee;
}

or:

function foo() {
  var caller = arguments.caller;
}

would be bad code that we want to avoid.

The alternative would be to reference the caller and callee functions directly. So arguments.callee would be foo .

Conclusion

We should use === and !== for comparison to avoid the confusing outcomes of the == and != comparison operators.

If we use accessors, then we should group the same value accessors together.

Also, maximum classes per file should be limited. The fewer the better. This limits any code file to a single responsibility.

Alerts, confirmation, and prompt pop-ups should be used as sparingly as possible.

Finally, we shouldn’t use arguments.caller and arguments.callee in traditional functions.

Categories
JavaScript Best Practices

JavaScript Best Practices — Variable Naming

JavaScript is a very forgiving language. It’s easy to write code that runs but has issues in it.

In this article, we’ll look at best practices for naming JavaScript variables.

Computed-Value Qualifiers in Variable Names

We should name variables that hold computed data with a modifier that the end of the name.

For instance, if we have a variable that calculates a total, then we should have it with the Total at the end of the name.

If it holds the total price of a purchase, for example, we can write priceTotal .

As long as we stick with a convention for the qualifier, then no one will get confused.

Less confusion means easier to read code.

If a computed value goes that the end of the name as a rule, then we can put the modifier before it.

For instance, we can write totalPrice instead.

Common Opposites in Variable Names

If we have variables that hold the opposite data of another, then we should be precise with them.

For instance, if we have a begin variable, then we can have the end variable if it holds the opposite data of the begin variable.

Likewise, there’re also names like min and max , next and previous and so on.

Naming Loop Indexes

Loop indexes should have short variable names like i , j and k as a convention.

For instance, we may write:

for (let i = 0; i < 10; i++) {
  //...
}

However, if we have nested loops, then we may want to name the variables with a more descriptive name to make the loops more readable.

If we have a nested loop, we may write:

for (let eventIndex = 0; eventIndex < 10; eventIndex++) {
  //...
  for (let teamIndex = 0; teamIndex < 10; teamIndex++) {
    //...
  }

}

Naming Status Variables

We should name flags with a descriptive name so that we know what they hold.

For instance, we should have a flag with a name with something like isSystemReady so that we know that the flag is for holding information on whether a system is ready.

Naming Temporary Variables

Temporary variables are used to hold intermediate results of computations.

We shouldn’t treat them casually since they’re still part of the code.

It’s better that we define temporary variables with a descriptive name.

For instance, instead of writing:

const temp = (b ** 2 - 4 * a * c) ** 0.5;
let solutions = [];
solutions[0] = (-b + temp) / (2 * a);
solutions[1] = (-b - temp)

We write:

const discriminant = (b ** 2 - 4 * a * c) ** 0.5;
let solutions = [];
solutions[0] = (-b + discriminant) / (2 * a);
solutions[1] = (-b - discriminant)

Then we know that (b ** 2–4 * a * c) ** 0.5 is the discriminant of the quadratic formula.

Naming Boolean Variables

Booleans also need descriptive names.

There’re a few that pertain to boolean variables. They include done to indicate that something is done.

error to indicate whether we have an error or not.

found to indicate that something we look for is found.

success or ok indicates whether an operation has been successful.

Photo by Yoann Boyer on Unsplash

Give Boolean Variables Names that Imply true or false

The names above are good because they imply that they can only be true or false.

We may also want to put is in front of a variable to indicate that it’s a boolean.

For instance, we can define isFound instead of found ,

Use Positive Boolean Variable Names

Double negative expressions are hard on our brains. So we should have positive variable names like found instead of notFound .

Naming Constants

Constants usually are named with all upper case with underscores separating words.

For instance, we can define MAX_DONUTS to hold the maximum number of donuts someone can eat.

Why Have Conventions?

We should follow conventions to that things are easy to read and understand since people don’t have to switch context all the time.

It also prevents us from calling the same things with different names.

Conventions for names can also compensate for language weaknesses. So since JavaScript doesn’t have data type annotations, we can specify the type in the name.

Relationships among items are also indicated with consistent naming schemes.

Conclusion

We should name things with descriptive names and don’t use double negatives.

The only exceptions are loop indexes since often use letters for the names.

Constants should be all uppercase with underscores separating them.