Categories
JavaScript Best Practices

JavaScript Best Practices — Imports, Symbols, and 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 proper way to import modules, the use of the Symbol constructor, and calling super before referencing this in a child class’s constructor.

No Duplicate Imports

Duplicate imports should definitely not be added. If we import multiple items from the same module, then we should put them all in one statement.

For instance, we shouldn’t write something like the following if we want to import multiple items:

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

Instead, we can save some space by writing the following:

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

The more imports we’re importing, the more space we save by consolidating all the imports into one statement if we import multiple members from the same module.

Don’t Use the Symbol as a Constructor

In JavaScript, symbols are used as unique identifiers for methods in objects and classes.

Every Symbol is unique, even if they have the same name. For instance, if we have the following code:

const foo1 = Symbol('foo');
const foo2 = Symbol('foo');
console.log(foo1 === foo2);

Then the console lot output logs false since each symbol returned by the Symbol function is their own instance.

The Symbol function isn’t a constructor. Therefore, we shouldn’t use the new operator with it.

For instance, we shouldn’t write something like the following code:

const a = new Symbol("a");

If we run that, then we’ll get the error message ‘Uncaught TypeError: Symbol is not a constructor’.

Instead, we should just call it directly as follows:

const a = Symbol("a");

The Symbol function should be called directly as a function.

Don’t Use this or super Before Calling super() in Constructors

In JavaScript, if we create a class that extends another class using the extends keyword, then we’ve to call super to call the parent class’s constructor within the child class.

If we reference this before calling super , then we would get a ReferenceError.

For instance, if we have the following classes:

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

class Cat extends Animal {
  constructor(name, breed) {
    this.breed = breed;
    super(name);
  }
}

const cat = new Cat('jane', 'black')

In the code above, we’ll get the error message ’Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor’.

This is because we have to call super to make this defined. The parent constructor has to be called first to finish the configuration of this before the subclass starts the configuration of this .

Superclass doesn’t know about subclasses, so superclasses must be instantiated first.

Therefore, we need to call super before running any code that references this so that we won’t get this error.

The Cat class must be changed so that any code that references this must be moved below the super function’s call.

We should instead write:

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

class Cat extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

const cat = new Cat('jane', 'black')

In the code above, we have the super call that comes before this.breed = breed; in the constructor method.

Now we won’t get any errors when we run the code.

However, code that references this in any other method can be anywhere. For instance, if we have the following code:

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

  speak() {}
}

class Cat extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  meow() {
    console.log(this.name);
    super.speak();
  }
}
const cat = new Cat('jane', 'black')
cat.meow();

In the code above, we have the speak method in the Animal class. In the Cat class, we have the meow method that references this.name before calling a method from the parent Animal class.

Conclusion

If we import multiple members from the same module, then we should put them all in one statement to save space.

The Symbol function isn’t a constructor function. To create a new symbol, we should create it by calling the Symbol function.

To make sure that our parent class instance is configured properly, then we should call super in our child class’s constructor first and then run any code that references this .

Categories
JavaScript Best Practices

JavaScript Best Practices — Module Imports 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 look at the best practices for adding module imports to our JavaScript code and best practices for using iterators and generators.

Multiline Imports Should Be Indented Just Like Multiline Array and Object Literals

Multiline imports should be indented like multiline arrays and object literals.

This means that if we import multiple members from a module and each member is in their own line, then they should have 2 spaces for indentation.

For instance, we write the following to import multiple members in one import statement:

module.js

export const foo = 1;
export const bar = 2;
export const baz = 3;
export const qux = 4;
export const longMember = 5;
export const longMember2 = 5;
export const longMember3 = 5;
export const longMember4 = 5;
export const longMember5 = 5;

index.js

import {
  foo,
  bar,
  qux,
  baz,
  longMember,
  longMember2,
  longMember3
} from "./module";

In the code above, we have many members that are exported from module.js and imported in index.jhs .

Since we have lots of members to import and many of them with long names, we should separate them all into their own line with 2 spaces to indent each import to keep everything neat and not overflowing many people’s screens.

No Webpack Loader Syntax in Module Import Statements

Module import statements for style files should follow the module syntax so that it won’t be coupled to the module bundler of our choice.

This makes sense since module bundlers may change in our project depending on our needs.

Therefore, instead of writing the following code:

import stylesCSS from "style!css!styles.css";

We should instead write:

import stylesCSS from "./styles.css";

The 2nd example is standard syntax for import style files, so we should use that instead.

Do Not Include JavaScript Filename Extensions

We don’t need to include the extension for importing modules, so we shouldn’t include it in our import code.

For instance, instead of writing the following:

module.js

export const foo = 1;

index.js

import { foo } from "./module.js";
console.log(foo);

We should write:

import { foo } from "./module";
console.log(foo);

The JavaScript interpreter is smart enough to identify modules by their name rather than their extensions.

Don’t Use Iterators. Prefer JavaScript’s Higher-Order Functions Instead of Loops Like for-in or for-of

JavaScript arrays have many methods that we can use to manipulate arrays, sorting, searching, mapping values, etc. without having to write a single loop.

Therefore we should take advantage of them instead of writing out loops explicitly.

For instance, if we want to map each entry of an array from one value to another, instead of writing a loop as follows:

const arr = [1, 2, 3];
const result = [];

for (const a of arr) {
  result.push(a ** 2);
}

In the code above, we have an array arr with some numbers in it. Then we have a for...of loop to loop through the values of an array. In the loop body, we called push on the result , which is initially empty, to push the items into the result array.

This is a lot longer than using the map method with a callback to map array entries into items in a new array with new values.

Then we get that the value of result is [1, 4, 9] .

For example, we can use map to do the same thing as follows:

const arr = [1, 2, 3];
const result = arr.map(a => a ** 2);

In the code above, we called the map method with a callback that maps each array entry taking their value and then return the square of each value.

In the end, we get the same value for result , which is [1, 4, 9] . However, it’s much shorter than what we had before with the for...of loop.

Therefore, we should use higher-order array methods like map , reduce , every , some , sort , filter , find , findIndex , etc. as much as possible.

We can chain them if they return an array.

Conclusion

We should use higher-order array methods instead of using loops to do most array processing. They’re much more concise and we can chain them if they return an array.

Imports don’t need an extension for the module part of the statement. If we import lots of members, then we should separate the members in their own lines with 2 spaces for indentation before each member.

Categories
JavaScript Best Practices

JavaScript Best Practices — Generators and Rest/Spread Spacing

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 preventing the use of useless generators and the spacing rest and spread operations.

Always Add yield Statements to Generator Functions

The whole point of generator functions is that it returns a series of values sequentially when the generator is called to create a generator that returns those values.

To return the values, we’ve to use the yield keyword to return the items.

If we’re just returning items without using the yield statement, then we can just use a regular function to do that.

For instance, the following shouldn’t be a generator function:

function* foo() {
  return 1;
}

The because to get the return value from it, we’ve to write the following:

const {
  value: val
} = foo().next();

In the code above, we called the foo generator to return a generator, then we’ve to call next to get the return value.

The object returned has the value , which is the return value 1 and the done property, which is true since all it does is return the value in the generator function.

As we can see, this is a very roundabout way to return something when we can just return the value directly with a regular function.

We can just write:

const foo = () => 1;

to create a regular function that returns 1.

Therefore, if we’re going to define a generator function, we should make sure that we’re returning a series of values with the yield keyword.

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

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

With the yield keyword we return the value and pause the generator. Then when we call next to invoke it again, we resume the generator and return the next value.

Then we can create a generator by calling foo and loop through the values as follows:

for (const f of foo()) {
  console.log(f);
}

Spacing Between Rest and Spread Operators and Their Expressions

The rest and spread operator are both denoted by ... . While they’re denoted by the same symbol, they’re used differently.

The rest operator is used to get the arguments into an array from the arguments that are called that haven’t been assigned to a value of a parameter.

It’s also used to get the array entries that haven’t been destructured into their own variable into an array.

If the rest operator is used to destructure objects, then the object entries that haven’t been destructured are stored in the variable that’s applied with the rest operator.

The spread operator is used to make shallow copies of arrays and objects and also merge a multiple of them together.

Also, the spread operator can be used to spread an array into an argument when it’s used on an argument of a function.

They both come before the operand. Usually, we don’t have any spaces between the operator and the operand when the operator is the rest or spread operator.

For instance, we usually write something like the following:

const a = {
  foo: 1
};
const b = {
  bar: 2
};
const c = {
  baz: 3
};
const merged = {
  ...a,
  ...b,
  ...c
};

As we can see from the code above, we don’t have any space between the spread operator and a . Likewise, we have the same spacing as objects b and c .

With arrays, we have the same spacing, so we write something like the following:

const a = [1, 2];
const b = [3, 4, 5];
const merged = [...a, ...b];

In the code above, we also don’t have any spaces between the spread operator and a . It’s also the same with b .

If we use the spread operator when calling a function with an array of arguments, we write:

const add = (a, b) => a + b;
const sum = add(...[1, 2, 3]);

We also didn’t add any spacing between the operator and the operand. Also, we didn’t any spaces anywhere else within the parentheses.

However, if we use it with other arguments, then we need some spacing between the argument. We write the following, for example:

const add = (a, b) => a + b;
const sum = add(1, ...[2, 3]);

In the code above, we have a space character after the comma and before the spread operator.

Likewise, with the rest operator, we write the following. If we’re using it on a function, then we write:

const add = (...args) => args.reduce((a, b) => a + b, 0);

We don’t have any spaces inside the parentheses. This is consistent with the previous example where we used the spread operator.

If we have other parameters before the rest parameter, then we need some spacing. For instance, we can define a function as follows:

const add = (a, ...args) => a + args.reduce((a, b) => a + b, 0);

As we can see, we have a space character after the comma.

With destructuring, we write something like the following:

const [a, ...b] = [1, 2, 3];
const {
  c,
  ...rest
} = {
  c: 1,
  d: 2,
  e: 3
}

The spacing is consistent with the other examples, except with object destructuring, where we put each variable on their own line for clarity.

Conclusion

The yield statement should be in JavaScript generator functions to pause and resume the returned generator. Otherwise, we should just define a regular function.

The spacing of the rest and spread operator relative to other entities is standard with one space between some arguments or variables. If there are no other arguments or variables, then no space is required.

There’s definitely no space between the rest and spread operator and its operand.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes and 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 best practices for dealing with classes and constructors.

Always Use the Class Syntax. Avoid Manipulating Prototype Directly

The class syntax in JavaScript is just syntactic sugar on top of the constructor function syntax.

To create a constructor function with an instance method, we have to write something like the following code:

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return `hi ${this.name}`;
};

In the code above, we have the Person constructor function, which has the name parameter. We set the name parameter to this.name .

Then we defined the greet instance method, we have the returns the string that has the word ‘hi’ with this.name .

this.name would be the name member that’s in the Person instance.

If we log the value of the following expression:

new Person('jane').greet()

Then that returns the string 'hi jane’ .

The old constructor function makes creating constructors harder. We’ve to add instances to its prototype property, which means that we can’t put all the members in one place.

Instead, we should use the class syntax, which lets us keep all the members inside one class.

For instance, with the class syntax, we can rewrite the code as follows:

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

  greet() {
    return `hi ${this.name}`;
  };
}

In the code above, we have the Person class, which encapsulates all the members of the constructor, including instance variables and methods, all in one entity.

That’s much better than adding properties to a prototype. Also, we make sure that we have our initialization code in the constructor method instead of within the function itself.

This is much clearer as we know for sure that the value of this is the Person instance. It’s not so clear what the value of this is in the constructor function example by reading the code.

Use extends for Inheritance

The extends keyword that we can add to a class makes doing inheritance with constructor function easily.

With it, we can create a child class that inherits from the parent class without creating constructor functions that have to call and modify prototype .

We’ll also get an error if we forgot to call the parent constructor by calling super in the constructor if we have the extends keyword.

For instance, instead of writing the following code with constructor functions as follows:

function Animal(name) {
  this.name = name;
}

Animal.prototype.greet = function() {
  return `hi ${this.name}`;
}

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Animal;

In the code above, we have to do many things to create a Dog constructor as the child of the Animal constructor.

We’ve to call the Animal constructor by writing Animal.call(this, name); . Then we have to create Dog ‘s prototype by calling Object.create with Animal ‘s prototype as we did with:

Dog.prototype = Object.create(Animal.prototype);

Also, we have to set the parent constructor for Dog to Animal by writing:

Dog.prototype.constructor = Animal;

As we can see, we need many lines to create a Dog constructor that inherits from Animal .

We can only make sure that we did that all that correctly by calling the greet method from the Dog instance as follows:

new Dog('jane').greet()

Since we did inheritance with constructors correctly, we can call the Animal constructor with ease.

We instead write the following with the class syntax:

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

  greet() {
    return `hi ${this.name}`;
  };
}

class Dog extends Animal {
  constructor(name, breed) {
   super(name);
    this.breed = breed;
  }
}

All we have to do in the code above is calling the parent constructor from Dog ‘s constructor with super and add the extends keyword. If we forgot to call super , we’ll get an error.

Also, the class member code is all within the curly brace so we can tell what

Therefore, using the class syntax with extends is much clearer. There’s also less chance for mistakes.

Conclusion

The class syntax should always be used instead of the constructor syntax. It’s better for defining standalone constructors and also for inheriting from other constructors.

The class syntax is equivalent to constructor functions.

Categories
JavaScript Best Practices

JavaScript Best Practices — Spread Syntax and Template 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 using the spread syntax for calling functions and the use of template strings instead of using string concatenation.

Using Spread Syntax Instead of .apply()

The spread syntax lets us call functions with an array spread into a comma-separated list of arguments in our function.

It’s a lot easier than the old way, where we have to call apply method on a function with an array of arguments to call a function with many arguments.

For instance, with the old apply method, to get a minimum of an array of numbers with the Math.min method, we’ve to write the following code:

const min = Math.min.apply(null, [1, 2, 3, 4, 5]);

In the code above, the apply method call’s first argument is the value of this , which is null because Math.min is a static method, so we don’t need to set a value for this .

The 2nd argument is the array of arguments that we want to pass into the method.

Therefore, we get the value 1 for min , which is the smallest value in the array.

With the spread syntax on function calls, we don’t have to do all that to call a function to spread an array of arguments into a comma-separated list of arguments of a function.

We can just use the ... operator to spread an array of arguments into a comma-separated list of arguments.

To call Math.min with the spread syntax, we can just write the following:

const min = Math.min(...[1, 2, 3, 4, 5]);

As we can see, we get the same return value of 1 from the method call, but we’ve to type a lot less to do the same thing. Also, we don’t have to use the apply method to call it with a list of arguments.

Using Template String Literals Instead of String Concatenation

Template string literals is another great feature that’s released with ES6. It lets us interpolate expressions into our strings so that we get a dynamic string.

This is much better than string concatenation since that gets confusing very quickly with all those + signs to concatenate strings.

For instance, instead of writing the following code:

const greeting = 'hi';
const firstName = 'jane';
const lastName = 'smith';
const greetingStr = greeting + ', ' + firstName + ' ' + lastName;

which is confusing when we read it with all the + signs and quotes. We write the following code with template strings:

const greeting = 'hi';
const firstName = 'jane';
const lastName = 'smith';
const greetingStr = `${greeting}, ${firstName} ${lastName}`;

In the code above, we used template string literals, which lets us interpolate expressions into our string with the ${} delimiter.

We insert the greeting , firstName , and lastName variables straight into the template string literal.

It’s much clearer since we don’t have to deal with the + operators and quotes everywhere.

Also, template strings are delimited by backticks, so we can use single and double quotes for quoting text rather than as string delimiters.

It’s also sensitive to spaces and line breaks so we can use it to make multiline strings without much trouble.

To make a multiline string, we just have to write the following:

const greeting = 'hi';
const firstName = 'jane';
const lastName = 'smith';
const greetingStr = `${greeting},
${firstName} ${lastName}`;

Then we get that the value of greetingStr is:

hi,
jane smith

That’s great since the old way of creating multiline strings is with the linebreak character and concatenation.

So we had to write something like the following to create a multiline string:

const greeting = 'hi';
const firstName = 'jane';
const lastName = 'smith';
const greetingStr = greeting + ',n' + firstName + ' ' + lastName;

In the code above, we added an extra n after the , to create a new line.

If we have more newline characters to create line breaks, then the code will get more confusing.

Conclusion

The spread syntax and template string literals are both great features of modern JavaScript.

The spread syntax lets us spread an array into a comma-separated list of arguments in our JavaScript code.

Template string literals let us interpolate JavaScript expressions into our code, which is much better than concatenating them all together. It also lets us create multiline strings without adding line break characters explicitly.