Categories
JavaScript Best Practices

JavaScript Best Practices — Cases and Loops

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 how case statements should be used and the best practices for writing loops.

Order Cases Alphabetically or Numerically

We may want to order our cases alphabetically or numerically to read them easier.

For instance, we can write:

switch (val) {
  case 1:
    //...
    break;
  case 2:
    //...
    break;
  default:
    //...
    break;
}

or write:

switch (val) {
  case 'a':
    //...
    break;
  case 'b':
    //...
    break;
  default:
    //...
    break;
}

Put the Normal Case First

If we put the normal case first, then we don’t have to go to the other cases as often because of short-circuiting.

So if we have the 'success' case as the normal case, we can write:

switch (val) {
  case 'success':
    //...
    break;
  case 'error':
    //...
    break;
  default:
    //...
    break;
}

Keep the Actions of Each Case Simple

Cases should be short to keep them readable. If the actions inside the case statement or block are complex, we should write functions to contain them.

For instance, we can write the following:

switch (val) {
  case 'success':
    handleSuccess();
    break;
  case 'error':
    handleError();
    break;
  default:
    //...
    break;
}

Don’t Make Up Phony Variables to be Able to Use the case Statement

A case statement should ve used for data that can easily be categorized.

Therefore, we should write something like:

const action = command[0];
switch (action) {
  case 'a':
    //...
    break;
  case 'b':
    //...
    break;
  default:
    //...
    break;
}

The variables that we pass into switch should be a simple variable. We shouldn’t have to do something like taking the first letter from a string before checking the cases.

The first letter can be used by more than one word, so we want to avoid using the first letter for our cases.

Use the default Clause Only to Detect Legitimate Defaults

default clause should only be used for real defaults.

If we don’t have a real default value, then we don’t need a default clause.

Use the default Clause to Detect Errors

If there’re cases that aren’t supposed to happen, we can use the default clause to catch those cases.

For instance, we can write something like:

switch (action) {
  case 'a':
    //...
    break;
  case 'b':
    //...
    break;
  default:
    handleError();
    break;
}

Then we handle errors in the default case in case it’s encountered.

Avoid Dropping Through the End of a case Statement

JavaScript doesn’t automatically break at the end of each case statement or block.

Therefore, we should write the break keyword to stop the execution at the end of the statement or block.

For example, instead of writing:

switch (action) {
  case 'a':
    //...
  case 'b':
    //...
}

We write:

switch (action) {
  case 'a':
    //...
    break;
  case 'b':
    //...
    break;
}

When to Use a while Loop

We should use while to test conditions for the exit at the beginning of the end of the loop.

To do that, we write:

while (!done) {
  if (condition) {
    done = true;
  }
  //...
}

This will check the condition at the beginning of the loop body.

We can also check at the end by moving the if block to the end.

Also, we have the do...while loop to run the loop body first before checking the condition for continuing the loop.

For instance, we can write:

do {
  if (condition) {
    done = true;
  }
  //...
}
while (!done)

Then the first iteration always runs.

Use a Loop-With-Exit Loop

We can also put a loop condition inside the middle of the loop body.

Then we can avoid errors that result from any invalid values that are produced before the exit check.

For instance, we can write:

while (!done) {
  //...
  if (length < 0 || width < 0) {
    done = true;
  }
  //...
}

Then if length or width are less than 0, we can avoid running the rest of the code that need them.

Conclusion

We can use the while loop to repeated run code and check for the condition in each iteration so we can end it.

There’s also the do...while loop to always run the first iteration before checking our condition.

If we use switch statements, we may want to order our case statements in order, and always have break at the end of the block.

Alternatively, we can also put the normal case first, and the error cases later.

Categories
JavaScript Best Practices

JavaScript Best Practices — With, Promise Errors, Regex Issues

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

In this article, we look at issues with the with statement, named capture groups, rejecting errors with Error object, and using regex literals instead of the RegExp object.

Remove with Statements

with statements are bad. They let us add member objects to the current scope, which makes it impossible to tell what the variable inside the block refers to.

For instance, if we have the following code:

const obj = {
  a: 1,
  b: 2
};

with(obj) {
  a = 3;
}

Then the code is within the with block is written relative to the obj object. So a inside the block is actually referencing obj.a .

However, we can also declare new variables as follows:

const obj = {
  a: 1,
  b: 2
};

with(obj) {
  let a = 3;
}

which is block-scoped and can’t be accessed outside the with block.

Or we can declare a variable with var as follows:

const obj = {
  a: 1,
  b: 2
};

with(obj) {
  var a = 3;
}

which changes obj.a to 3.

As we can see the scope and of a varies depending on how we declared the variable inside with . We’re either modify the obj object’s properties or we’re declaring a new block-scoped variable if we use let or const .

Therefore, the with block is confusing and shouldn’t be used in our production code.

It doesn’t make writing our code easier, and it just makes our code more confusing to everyone.

Therefore, it should never be used in our code.

Use Named Capture Group in a Regular Expression

Named capture groups are good in regular expressions because it’s clear that where the capture groups are and what they’re checking for.

For instance, we can write something like the following:

const regex = /(?<email>[^@]+@[^.]+..+)/;

In the code above, we have a capture group called email that has the email regex after it.

Now instead of just having a bunch of symbols in our regex, we know that our regex capture group is checking for an email address.

Then we can retrieve the result of the capture group by using the exec method as follows:

const result = regex.exec('abc@abc.com').groups.email;

As we can see, the groups property of the object that’s returned by exec has the email property which has the match for our email capture group.

This is much better than just having a bunch of symbols without a name. We can’t get those with a named property.

Use Error Objects as Promise Rejection Reasons

Using error objects as promise rejection reasons is a good idea because it automatically stores the stack trace.

The stack trace is useful for debugging issues that caused the error since it tells us what functions are called to get to the final error.

If we don’t reject an error, then we have no stack trace and we don’t know what’s been called before the error.

For instance, instead of writing:

Promise.reject('error');

We should write:

Promise.reject(new Error('error'));

If we run both and look at the console log output, we’ll see that the 2nd example has the stack trace in the console log output and the first one doesn’t.

Use Regular Expressions Literals instead of the RegExp Constructor

In JavaScript, there’re 2 ways to construct regexes. One is the regular expression literals like /foo/ . The other way is to create an RegExp instance like new RegExp('foo');

Regular expressions are shorter and we can’t generate a regex dynamically with literals. The RegExp constructor takes a string with the regex that we want to create, so we can create a regex dynamically with a string.

However, if we want to add escape characters with the RegExp constructor, we’ve to add an extra backslash before the escape character so that we can write the escape character in the regex string as follows:

new RegEx('d+$');

Because of the extra complexity, it’s better to use regex literals instead of using the RegExp constructor. The regex above would be the same as:

/d+$/

if it’s written as a regex literal.

Conclusion

with statements should never be used in our code since it changes the scope of the variables inside.

Error objects should be used to reject promises so that we get a stack trace in our console log.

If we construct regexes, we should use the regex literals as it’s simpler. Named capture groups should be used so that we can retrieve the capture group match with a name and also makes the pattern easier to understand.

Categories
JavaScript Best Practices

JavaScript Best Practices — This and Class Strings

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 how to return this to let us chain class methods together and write a toString method to make sure that our class works properly and causes no side effects.

Methods Can Return this to Help With Method Chaining

In our JavaScript classes, we can return this in our method so that we can chain our method with the modified value of this .

In each method that returns this , we can modify the value of this so that we can write code to chain those methods together and get the result that we want when the methods are called together.

For instance, we can write code like the following:

class Box {
  setHeight(height) {
    this.height = height;
    return this;
  }

  setWidth(width) {
    this.width = width;
    return this;
  }
}

In the code above, we have the Box class with the setHeight and setWidth methods.

In each method, we change one property of this with the value that we pass in as the argument of each method.

Then we can chain our Box instance method calls as follows:

new Box().setHeight(10).setWidth(15);

Then in our console log, we have the following output if we log the return value of the method chain expression that we have above:

Box {height: 10, width: 15}

As we can see, the height and width properties are set by the chain of method calls we have above.

Write a toString() Method in Our Class to Make Sure That it Works Successfully

We can write our own toString method in our JavaScript class so that we can see that it works successfully. In the method, we can return the value of the members of the class so that we know that they’re set correctly.

For instance, we can write the following code to add a toString method to our class:

class Box {
  setHeight(height) {
    this.height = height;
    return this;
  }

  setWidth(width) {
    this.width = width;
    return this;
  }

  toString() {
    return `height: ${this.height}, width: ${this.width}`;
  }
}

In the code above, we have the toString method that returns a string that has the value of this.height and this.width , which are the members of our Box instance.

Then when we call the following chain of methods:

new Box().setHeight(10).setWidth(15).toString();

We see that the value returned from toString is:

height: 10, width: 15

And we confirm that the members are set correctly.

An Empty Constructor Function or One That Just Delegates to a Parent Class is Unnecessary

In JavaScript, we don’t have to provide a constructor for every class. We also don’t need to call the parent class constructor from a child class if we just call it with the same arguments as the parent class.

For instance, we instead of writing the following code to add empty constructor:

class Foo {
  constructor() {}
}

We can just skip the constructor method inside the Foo class as follows:

class Foo {}

They both do the same thing, so we don’t need the extra empty constructor method.

Also, we don’t need code in our child constructor that just calls the parent constructor:

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Cat extends Animal {
  constructor(...args) {
    super(...args);
  }
}

In the code above, we have the super call in Cat that just calls the parent Animal constructor with whatever we pass in.

When we create a Cat instance as follows:

const cat = new Cat('jane');

We get the value Cat {name: “jane”} from the console log.

We can just omit the constructor in this case. Therefore, we can just write the following:

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Cat extends Animal {}

Without the constructor in Cat , we get the same result as in the previous example, so we should just take it out.

Conclusion

Our class methods can return this to return the value of this after it’s been modified by our method.

If we just have an empty constructor in a parent class or a constructor in a child class that calls super with the same arguments as the parent constructor, we can remove them from our code.

Also, we can make a toString method to return a string with our class members to make sure that they’re set correctly.

Categories
JavaScript Best Practices

JavaScript Best Practices — Sorting Imports, Symbols, String Expressions, and Generators

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

In this article, we’ll take a look at whether we should sort imports in our code, the proper way to define symbols, the spacing of delimiters of expressions in template strings, and calling other generator functions with yield*.

Import Sorting

Sorting imports alphabetically sometimes helps us with looking up the import manually.

For instance, if we have:

import {foo, bar} from "./module";

Then foo and bar aren’t alphabetical order. It may be easier if we instead write:

import {bar, foo} from "./module";

So that we know all imported members are always listed in alphabetical order.

Likewise, if we import multiple modules, we may also want to list the modules in alphabetical order.

For instance, we can write the following code to do that:

import {bar, foo} from "./moduleA";
import {baz} from "./moduleB";

In the code above sorted the imports by their module name. We have moduleA ‘s imports coming before moduleB .

This is more convenient if we ever have to look up import manually.

However, if we have to sort the imports manually in our code, then it’ll be a pain. Therefore, we’ve to think about the benefits over the time spent sorting imports if we’re going to do that.

Symbol Description Should Be Required

In JavaScript, a symbol is a primitive value that’s used as a unique identifier for object and class methods. It’s used as an alternative to strings or numbers as keys of methods.

It has the type 'symbol' when we apply the typeof operator on it.

We can define a symbol by using the Symbol factory function. For instance, we can write the following code to do that:

const foo = Symbol("foo");

In the code above, we defined a new symbol by calling the Symbol function with a string description.

Every instance of a symbol is different. Even if we create another one with the same string passed in it, it’ll reference a different symbol in memory,

This is needed for readers to identify them. Without the description, we can’t identify it.

For example, if we write the following:

const foo = Symbol();

Then there’s no way for us to identify the symbol that we just created. Debugging is much easier if our symbols have a description string passed in so that we can identify them when we’re debugging.

Spacing in Template Strings

When we use template strings, we usually use them to interpolate JavaScript expressions inside our strings. If we’re doing that, then we need to add the delimiter ${} into our string to wrap the expression that we’re interpolating in between the curly braces.

We usually don’t put any spaces between curly braces and the expression. For instance, we write something like the following in our template strings:

const person = {
  name: 'jane'
};
const str = `hi, ${person.name}`;

In the code above, we interpolated the expression person.name into our template string. And we didn’t put any space between the curly braces and the expression.

This is typical spacing as it’s clear where the expression starts and ends without spaces so we can just skip the spaces.

Spacing Around the * in Yield* Expressions

If we’re calling another generator function within a generator, then we need to use the yield keyword with the * beside it.

For instance, if we have the following code:

function* bar() {
  yield 1;
  yield 2;
}

function* foo() {
  yield* bar();
}

Then our foo generator function is calling the bar generator function to return a generator that returns the values of the bar generator function.

It has the same mechanisms as any other generator function. It returns the first value when we call next on it, then pauses, and then when we call next on it again, it returns the next value.

In the foo function above, we don’t have any spaces between the yield keyword and the * . This is standard spacing so that everyone can tell that we’re calling another generator function within foo .

Conclusion

We may want to sort our import statements to make them easier to spot.

Also, when we define symbols, we should always provide a description string so that we can look them up when reading and debugging the code.

Spacing around the curly braces should be standard. There’s usually no spaces between the expression and the curly brace in template strings.

Finally, when we call generator functions within a generator function, we usually write yield* .

Categories
JavaScript Best Practices

JavaScript Best Practices — Objects and Useless Constructors

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 the use of useless property keys and useless constructors in classes.

No Useless Computed Property Keys in Objects and Classes

Since ES6, we can name dynamic property keys in our objects and classes.

They’re denoted by brackets and we can put in strings, numbers or symbols in it to create computed property names.

For instance, we can write the following code to add dynamic computed property keys as follows:

let foo = 'fo' + 'o';
class A {
  [foo]() {}
}

In the code above, we have the foo variable which is used as a dynamically computed method key in our A class.

This allows us to create keys that are derived from other sources. With this, we can create dynamic member keys from strings that are created from various sources like template strings, concatenation, combining strings and numbers, etc.

Anything that returns strings, numbers, or symbols can be used as computed property keys.

Likewise, we can use dynamic property keys with objects. For instance, we can write the following code to create a dynamic property for an object:

let foo = 'fo' + 'o';
const obj = {
  [foo]() {}
}

In the code above, we created the obj object with the foo dynamic property key so that we can create a computed property key that uses a string that’s created by concatenating 2 strings.

With this syntax, it’s easy to create useless dynamic property keys like the following:

const obj = {
  ['foo']() {}
}

In the code above, we have the ['foo'] dynamic property key. This isn’t very useful since the string is constant. Therefore, we don’t need extra brackets.

And since the string is a valid identifier, we don’t need the quotes. Therefore, we can just rewrite it as follows:

const obj = {
  foo() {}
}

We removed the extra symbols that we don’t need and get the same result as before.

Likewise, we can do the same with class members. If we have the following class:

class A {
  ['foo']() {}
}

Then we can rewrite it as follows:

class A {
  foo() {}
}

We eliminated the brackets and the quotes since foo is a valid method identifier.

Likewise, we can do the same with numerical property keys in objects. Instead of writing:

const obj = {
  [0]: 1
}

We can write:

const obj = {
  0: 1
}

We didn’t need brackets to create a property that has numerical keys.

No Unnecessary Constructor

In JavaScript classes, we don’t always have to provide our own constructor. If we only want an empty constructor, then we don’t have to provide it ourselves.

For instance, if we have the following class:

class Foo {
  constructor() {}
}

Then we don’t need the constructor in the Foo class as the JavaScript interpreter will provide it for us.

Instead, we can write the following code:

class Foo {}

We can replace an empty constructor, then we don’t need to provide it in order to be able to instantiate our class.

For child classes, we don’t have to provide a constructor that calls super if the parent constructor is an empty constructor.

For instance, instead of writing the following code for the Bar class, we can write the following instead:

class Foo {}

class Bar extends Foo {
  constructor() {
    super();
  }
}

Instead, we can just write:

class Foo {}

class Bar extends Foo {}

We only need to call super if we want to pass in arguments to the parent constructor.

Therefore, we need it if we have a parent constructor that takes arguments as follows:

class Foo {
  constructor(a) {
    this.a = a;
  }
}

class Bar extends Foo {
  constructor(a) {
    super(a);
  }
}

If we don’t want to set the value of this.a from the child constructor, then we can also skip the constructor implementation as follows:

class Foo {
  constructor(a) {
    this.a = a;
  }
}

class Bar extends Foo {}

Therefore, we don’t need to implement constructor ourselves in JavaScript classes as an empty default constructor is provided if we didn’t provide anything.

Conclusion

We shouldn’t use the computed property syntax in a useless. If our computed property is constant and it’s not derived from combining other pieces of data, then we don’t need it.

Also, we shouldn’t add empty constructors in our JavaScript classes. We don’t need it if our constructor has no content.

We also don’t have to provide a constructor for a child class if we don’t want to call the parent constructor from it.