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.

Categories
JavaScript Best Practices

JavaScript Best Practices — Modules

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 for importing and exporting from JavaScript modules.

Only Import From a Path in One Place

If we import multiple members from one module, then all members should be imported in one import statement.

For instance, instead of writing the following code to import members:

module.js

export const foo = 1;
export const bar = 2;

index.js

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

In the code above, we have 2 lines to import 2 different members from the same module. That’s no good since we have lots of duplicate code in one place.

Instead, we should just put them all in the same module to save space and remove duplicate code.

For example, we can instead write the following:

module.js

export const foo = 1;
export const bar = 2;

index.js

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

In the code above, we imported the members of module.js in index.js all in one line. As we can see, we saved lots of typing and duplication since we combined the 2 lines into one.

Do Not Export Mutable Bindings

We shouldn’t export mutable entities because we don’t want to accidentally change them inside the module that it’s export from. For instance, if we have the following code:

module.js

export let foo = 1;
foo = 3;
export const bar = 2;

index.js

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

In the code above, we exported the foo member from module.js and then change the value of foo later.

Then when we import foo in index.js , we see that the value 3 is logged for foo .

Therefore whenever we change the value by reassigning it, the new value will be exported.

Therefore, we shouldn’t export members that have been defined with let since these changes may be hidden if we import it from a library and not code that we edit usually.

If foo is defined with const then we wouldn’t be able to reassign it to a new value after we export it.

In Modules With a Single Export, Prefer Default Export Over Named Export

If we have only one member to export in a module, then we should just export it as a default export.

The advantage of that is that we can name it whatever we want when we import it without the as keyword.

For instance, instead of writing the following:

module.js

export const bar = 2;

index.js

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

We can write the following instead:

module.js

const bar = 2;
export default bar;

index.js

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

In the second example, we exported bar as a default export. Then in index.js , we can import it with the name foo or any name instead of the name bar without using the as keyword.

This is convenient to avoid name clashes with imports if we have lots of imports.

Put All Imports Above Non-Import Statements

Imports should be above non-import statements since this makes everyone clear where the exports are.

For instance, instead of writing the following code:

bar.js

export const bar = 1;

module.js

export const foo = 2;

index.js

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

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

In the code above, we imported items from 2 different modules in index.js and the 2 import statements are far from each other.

We have the foo import separated from the bar import with a console.log call.

Import statements being scattered makes finding them hard.

Instead, we should write the following:

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

console.log(foo, bar);

to group all the import statements at the top of our code file.

Conclusion

We should group import statements together so that they can easily find the imports from the top of the code.

If we have only one member to export, then we can just export it as a default.

Also, we should export mutable bindings since they can be changed after they’re exported.

Categories
JavaScript Best Practices

JavaScript Best Practices — Functions

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 some best practices for defining and using functions.

Never Declare a Function in a Non-Function Block

A function should never be defined in a non-function block like a if block or within loops.

Functions within loops tend to cause errors since functions create a closure around the loop.

For instance, if we have the following code:

const funcs = []
for (var i = 0; i < 10; i++) {
  funcs[i] = () => i
}

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

The code above would log 10 10 times because we declared the loop variable with var , so the last value, which is 10, will be returned in each function that’s in the funcs array.

We don’t want that, so we shouldn’t declare functions on the fly inside non-function blocks.

Use Function Expressions Inside Blocks

Function expressions can’t be used before they’re defined. This means that if they’re in blocks, then they can’t be used before they’re defined within the block.

Also, function declarations within blocks aren’t valid JavaScript syntax, so they shouldn’t be defined inside blocks.

If we want to define a function within a block, then we’ve to use function expressions.

For instance, we can define one within an if block as follows:

if (true) {
  const foo = () => {
    console.log('foo.');
  }
}

The code above is valid JavaScript syntax, so we used const to define it so it’s only available within the block.

Therefore, we should remember to define functions as function expressions instead of using function declarations in blocks.

Never name a parameter arguments

We should never name function parameters with the name arguments since arguments is a special object that’s available within a traditional function.

If we have a parameter arguments , then that’ll overwrite the value of arguments with the value of the arguments parameter.

For instance, if we have the following code:

function foo(arguments) {
  console.log(arguments);
}

foo(1)

Then the value of arguments is 1. On the other hand, if we have the following code:

function foo() {
  console.log(arguments);
}

foo(1)

Then we get the actual arguments object, which is:

Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ]

according to the console log output. It has the argument that we passed in and an iterator to iterate through the items or convert it to an array via Array.from or the spread operator.

Therefore, we should name our parameter something other than arguments .

Never Use arguments, Use Rest Syntax ... Instead

With the introduction of the rest operator, the arguments object is pretty much useless.

The rest operator takes the arguments that we passed in and put them into an array if it has been assigned to a parameter. It works with both arrow functions and traditional functions

On the other hand, the arguments object is only available within traditional functions and it’s an iterable array-like object rather than an array.

Therefore, to do anything useful with it, we’ve to convert it to an array, which is inconvenient.

For instance, instead of writing the following:

function foo() {
  console.log(arguments);
}

foo(1)

We should instead write:

function foo(...args) {
  console.log(args);
}

foo(1)

args would be an array with value 1 inside it.

Likewise, we can use arrow functions as follows:

const foo = (...args) => {
  console.log(args);
}

foo(1)

And we get the same result as before.

We can call any array method or do any array operations with rest parameters. For instance, we can write:

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

addAll(1, 2, 3);

to add all the arguments that are passed and add them together with the reduce method.

That’s something that we can’t do with the arguments object directly.

Conclusion

Rest parameters are much better than the arguments object for getting arguments that are passed into a function.

It works with both traditional and arrow functions.

We should never name a parameter with the name arguments since it clashes with the arguments object.

Declaring a function within a non-function isn’t valid syntax, so we shouldn’t do it.

We should only create functions as function expressions inside blocks.