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.

Categories
JavaScript Best Practices

JavaScript Best Practices- Loops and Useless Expressions

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 loops that we shouldn’t have, and useless labels, catch clauses, and call and apply calls that are useless.

No Unmodified Loop Conditions

If we have a while or do...while loop with an unmodified condition, the loop will run forever as the loop condition hasn’t been updated with a new value.

This is usually not what we want as it’ll crash our program most of the time because of the infinite loop.

However, infinite loops are sometimes appropriate for functions like generator functions that return the latest value when the generator is created from the function and invoked.

Therefore, in a regular while loop, we probably shouldn’t write an infinite loop:

while (condition) {
    doSomething();
}

However, it’s appropriate for generator functions that return an infinite number of entries:

function* num() {
  let i = 1;
  while (true) {
    yield i;
    i++;
  }
}

const iterator = num();
const val = iterator.next().value;
const val2 = iterator.next().value;

In the code above, we have the num generator function while returns a new integer after we created an iterator from it by calling it, and then calling next on it one or more times to get the value one at a time.

For regular while or do...while loops, we should almost always have a condition that changes.

For instance, we can write the following:

let x = 1;
while (x <= 5) {
  console.log(x);
  x++;
}

The code above has a condition that ends the loop as we increase x by 1 until x is 5.

No Unused Expressions

Some JavaScript expressions in our code are useless as it doesn’t do anything. For instance, if we have the following code:

x + 1;

Then it doesn’t do anything since we didn’t assign it to a variable and we didn’t return it in a function.

With build tools, expressions like these may be eliminated and break application logic.

Also, sequence assignment expressions like x = 1, y = 2 are always useless unless we return the values, assign them to another variable somewhere else, or do some manipulation with it later.

No Unused Labels

Unused labels are useless since they aren’t used anywhere. Therefore it’s dead code that’s just taking up space.

For instance, if we have the following:

loop:
  for (let i = 1; i <= 5; i++) {
    console.log(i);
  }

Either we should use it or we should remove it. We can use it as follows, for example:

loop:
  for (let i = 1; i <= 5; i++) {
    console.log(i);
    if (i === 2) {
      break loop;
    }
  }

We break the loop with the loop label with break loop when i is 2.

No Unnecessary .call() and .apply()

The call and apply method is only useful when we need to change the value of this in a function and call it with various arguments.

The following uses for it aren’t very useful:

function greet(greeting) {
  console.log(`${greeting} ${this.name}`);
}

greet.call(undefined, 'hi');

We passed in undefined to make this set to undefined . So it’s the same as calling greet('hi') directly.

Therefore, we don’t need to invoke the call method.

Likewise, the following apply call is also useless:

function greet(greeting) {
  console.log(`${greeting} ${this.name}`);
}

greet.apply(undefined, ['hi']);

It’s also the calling greet('hi') directly.

Instead, we should call greet with call or apply in ways that are useful.

For instance, we should call them as follows:

function greet(greeting) {
  console.log(`${greeting} ${this.name}`);
}

greet.apply({
  name: 'jane'
}, ['hi']);

greet.call({
  name: 'jane'
}, 'hi');

This way, we set the value of this inside the function to an object with the name property, so that this.name inside the function can be populated with the this.name .

We actually need to use call and apply to set the value of this in the example above.

Conclusion

Unmodified loop conditions are bad most of the time because it may cause crashes because of the infinite loop.

However, infinite loops are useful in generator functions that keep returning values with the returned iterator.

Unused labels for loops are useless and so it’s dead code that should be removed.

call and apply that are useless, like those that set the value of this to undefined since they are no different than calling the function directly.

Finally, we also shouldn’t have unused expressions in our code since it’s just more useless code that we can remove.

Categories
JavaScript Best Practices

JavaScript Best Practices — Generators and Object Properties

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 best practices when using generators and defining and using object properties.

Don’t Use Generators If We Want To Transpile to ES5

Generator code doesn’t transpile well to ES5 code, so we may want to think twice if we’re targeting our build artifact to build into ES5 code.

However, this shouldn’t be the case with modern transpilers since they create custom closure based state machines from generators and async functions. They work the same way. However, the only advantage is that the transpile code is harder to debug even with source maps.

Therefore, if we never have to debug the transpiled code, then we can go ahead and use generators. Otherwise, we should think twice before using them in our code.

Make Sure Generator Function Signature is Spaced Properly

Generator functions should be spaced properly. The asterisk for the generator function should come right after the function keyword.

For instance, we should define our function as follows:

const foo = function*() {
  yield 1;
}

In the code above, we have the asterisk coming right after the function keyword. And there’s no space after the asterisk.

For function declarations, we should write the following:

function* foo() {
  yield 1;
}

In the code above, we have one asterisk after the asterisk and before the function name.

Use Dot Notation When Accessing Properties

If an object property’s name is a valid JavaScript identifier, then we should use the dot notation to access the object property.

It’s shorter than the bracket notation and does the same thing.

For instance, instead of writing the following with bracket notation:

const obj = {
  foo: 1
}

console.log(obj['foo']);

We should write the following code:

const obj = {
  foo: 1
}

console.log(obj.foo);

In the first example, we used the bracket notation, which is longer and we have to access the foo property by passing in a string into the brackets. We have to write extra characters just to access the foo property.

Instead, we should write what we have in the 2nd example, which is obj.foo .

It’s shorter and does the same thing.

Use Bracket Notation [] When Accessing Properties With a Variable

If we want to access a property with the name that’s stored in the variable, then we should use the bracket notation to do that.

For instance, we can do that with the following code:

const obj = {
  foo: 1
}

const getProp = (prop) => {
  return obj[prop];
}

console.log(getProp('foo'));

In the code above, we have the obj object with the foo property. Also, we have the getProp function, which takes the prop parameter and returns the value of obj[prop] .

We’ve to access the property value with the bracket notation since prop is a variable, so there’s no other way to access the property dynamically.

Then in the last line of our example, we can use getProp as follows:

getProp('foo')

to return the value of obj.foo which is 1.

Use Exponentiation Operator ** When Calculating Exponentiations

The exponentiation operator provides us with a shorter way to calculate exponents. It’s shorter than Math.pow which is available since the first version of JavaScript.

The exponentiation operator is available since ES6. For instance, instead of writing the following with Math.pow :

const result = Math.pow(2, 5);

We should write:

const result = 2 ** 5;

It’s much shorter and we don’t have to call a function to do exponentiation anymore.

Conclusion

If we want to transpile our code to ES5 and debug the ES5 code that’s built, then we shouldn’t use JavaScript generators in our code.

Debugging is hard even with source maps.

If we do use generators in our code, then we should make sure the generator code is spaced properly. The * should be spaced in a standard for consistency and easiness to read.

When accessing object properties that are valid JavaScript identifiers, then we should use the dot notation. Otherwise, we should use the bracket notation. This includes accessing properties with variables and property names that aren’t valid JavaScript identifiers.