Categories
JavaScript Best Practices

JavaScript Best Practices — Function Signature and Arrow 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 look at how to format long function signatures and the best use of arrow functions.

Format Long Signatures by Putting Each Parameter in a New Line

If our function signature is long, then we should separate our arguments into a new line. For instance, we can write the following code to separate our arguments into their own line:

function foo(
  bar,
  baz,
  qux
) {}

In the code above, we have a foo function with 3 arguments bar , baz , and qux .

We separated each parameter into their own line, with , and a new line separating the parameters.

Likewise, we can do the same thing with a long list of arguments. For example, we can write the following code to put arguments into their own line for function calls:

foo(
  bar,
  baz,
  qux
)

In the code above, we have bar , baz and qux all in their own line. The comma and newline separate the arguments instead of just a comma.

When We Use an Anonymous Function, We Should Use Arrow Function Notation

Arrow functions are a great feature of JavaScript. It lets us define functions in a shorter way, and it doesn’t bind to its own value of this or arguments .

Also, we can return the last expression of the function as its return value if the expression that’s to be returned is in the same line as the function signature.

This is great for callbacks and other kinds of anonymous functions since we don’t have to deal with this and arguments with them most of the time.

For instance, if we call the array instance’s map method, then we need to pass in a callback.

Most of the time, we don’t need to manipulate this in our code, so we can just use arrow functions as callbacks.

For instance, we can write the following code to map our array entries into new values as follows:

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

In the code above, we called map on the array [1, 2, 3] . To do that, we passed in a function which maps the entry to a new value that’s squared of the value of the original entry.

Since the expression we’re returning is in the same line as the function signature and the arrow, it’ll return it without adding the return keyword explicitly.

If we want to return expressions that are more than one line long, then we need to wrap it around parentheses.

For instance, we can write a function to do the following:

const foo = () => ({
  a: 1
})

Then when we call foo , we get that its’ return value is:

{
  a: 1
}

In the function above, we wrapped the object around parentheses so that we return the object.

Arrow functions are much shorter than traditional functions since we don’t need the function keyword in all cases and the return keyword is omitted if the item we return is in the same line as the signature.

If we call the map method with a traditional function, then we have to write the following code:

const arr = [1, 2, 3].map(function(a) {
  return a ** 2
});

As we can see, our callback function now spans 3 lines instead of 1. And we have to type out the function keyword.

With all these benefits that arrow function brings, we should use them whenever we can. As long as we don’t need to reference this or use defines a constructor function, we can use it.

Photo by David Clode on Unsplash

Use Implicit Return for Returning an Expression Without Side Effects

As we can see from the examples in the previous sections, we should skip the braces and the return keyword if we have functions that return something on the first line of an arrow function.

We also should make sure that if an arrow function does an implicit return that it doesn’t commit any side effects.

For instance, given the map call we have in the example above:

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

In the function, we have a => a ** 2 so that we can return implicitly by skipping the braces and return keyword. Also, note that all it does is returning the expression and it’s not modifying anything outside the function.

Conclusion

Long function signatures and function calls should have parameters and arguments separated onto their own line.

Also, we should use arrow functions so that we can benefit from the features that it brings like conciseness and not having to worry about the value of this .

Categories
JavaScript Best Practices

JavaScript Best Practices — Designing Functions

Cleaning up our JavaScript code is easy with default parameters and property shorthands.

In this article, we’ll look at the best practices when designing functions.

Design at the Function Level

We got to design functions properly so that they can be worked on in the future without hassle.

The functions got to have high cohesion. This means that we only want to have relevant code in each function.

Anything unrelated shouldn’t be there.

However, there’re a few kinds of cohesion that aren’t good.

Sequential Cohesion

One of them is sequential cohesion, which means that each operation in a function must be done in a specific order.

We don’t want to get the birth date, then calculate the age and time for retirement afterward for example.

If we have a function that does both, then we should separate them into separate functions.

Communicational Cohesion

Communicational cohesion is another kind of cohesion that isn’t ideal.

Functions that use the same data and aren’t related in any other way shouldn’t be in one function.

For instance, if we have functions that log data and then reset them, then each operation should be in their own function.

Temporal Cohesion

Temporal cohesion is where operations are combined into a routine because they’re all done at the same time.

They encourage us to include code that are unrelated but has to be run at the same time.

In this case, we should separate those unrelated things into their own functions. and then run them under one umbrella function that has to be run at the given time.

For instance, we can write something like the following:

const showSplashScreen = () => {
  //...
}

const readConfig = () => {
  //...
}

const startUp = () => {
  showSplashScreen();
  readConfig();
}

Procedural Cohesion

Procedural cohesion is also bad. It means that the operations in a function has to be done in a specified order.

Things like a function to get a name, address, and phone number aren’t good since they aren’t really related, but they’re run in the same function.

It’s better to separate them out into their own functions and call them when needed.

Logical Cohesion

Logical cohesion is when several operations are put into the same function and they’re selected by a control flag that’s passed in.

Since they aren’t related to each other, we shouldn’t have those operations all in one function.

For instance, if we have:

const showSplashScreen = () => {
  //...
}

const readConfig = () => {
  //...
}

const doSomething = (option) => {
  if (option === 'splash') {
    showSplashScreen();
  } else if (option === 'read-config') {
    readConfig();
  }
}

Then we shouldn’t have the doSomething function.

Coincidental Cohesion

If a function has operations that have no relationship to each other, then that’s coincidental cohesion.

We should separate any code that isn’t related to each other into their own function.

Good Function Names

We got to name functions with good names. There are a few guidelines to following when we’re naming functions.

Describe Everything the Function Does

A function name should describe what the function does. So if it counts the number of apples, then it should be named something like countApple() .

We should have functions that only do one thing and avoid side effects so we don’t have to describe all of them in the name.

Photo by NordWood Themes on Unsplash

Avoid Meaningless or Vague Verbs

We want verbs that describe what the function does, so verbs like perform , process , or dealWith are too vague.

If a function is counting something then it should have the word like count or a synonym in the name.

Don’t Differentiate Function Names Solely by Number

Number names are not good, something like countApples1 , countApples2 , etc. aren’t good.

They don’t distinguish the difference between them by their name.

Make Function Names as Long as Necessary

A function name should be as long as necessary to describe everything that it does.

This way, everyone reading the code will know what a function does from the name.

Use a Description of the Return Value to Name a Function

If a function returns a value, then it should be named for whatever it returns.

So applesCount is good because we know that it returns the count of apples.

Conclusion

When we define functions, we should avoid various kinds of cohesion that don’t contribute to ease of reading and maintenance.

Also, we should name functions with descriptive names that describe everything that they do.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes and 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 what should be in our class methods, and also the best ways to use modules.

Class Methods Should Either Reference this Or Be Made Into a Static Method

A JavaScript class can have static or instance methods. If it’s an instance method , then it should reference this . Otherwise, it should be a static method.

For instance, we should write a class that’s like the following code:

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

  static greet() {
    return 'hi'
  }

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

In the code above, we have the static greet method which doesn’t reference this and the greetWithName method, which references this.name .

Static methods are shared with all instances of the class and instance methods are part of the instance, so it makes sense it references this .

Use ES6 Modules Import and Export Over Non-Standard Module Systems

With ES6, modules are a standard feature of JavaScript. We can divide our code into modules and only export code that we want to expose to the outside.

Also, we can selectively import members from another module.

Before ES6, there’re various module systems like RequireJS and CommonJS. They’re similar to what ES6 modules do today. However, ES6 modules can do what they do and they’re a JavaScript standard.

Therefore, we should just use ES6 modules over other kinds of modules so that we won’t have to worry about those kinds of modules going away or compatibility issues with other kinds of module systems.

For instance, instead of writing the following code to export and import modules:

module.js

module.exports = {
  foo: 1
};

index.js

const { foo } = require("./module");
console.log(foo);

We write:

module.js

export const foo = 1;

index.js

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

In the first example, we used module.exports to export a member as a property of an object.

Then we imported the foo property with the require function. This is the old way with CommonJS module which is used before JavaScript has modules as a standard feature.

The second example does the same thing using standard JavaScript modules. We export the member foo in module.js and then import foo inindex.js .

JavaScript modules have been a standard for a long time and it’s supported in both browsers and Node.js natively since version 12, we can use regular JavaScript modules anywhere.

If not, we can use transpilers like Browserify, Webpack, or Parcel to transform module code into ES5 code.

Do Not Use Wildcard Imports

Wildcard imports import everything. It’s denoted by an asterisk symbol.

For instance, we do a wildcard import by writing the following code:

module.js

export const foo = 1;

index.js

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

In the code above, we imported the whole module.js module by using the * and the as keyword to name the module that we imported so that we can reference it in the line below.

This isn’t good because we usually don’t need all the members of a module, so it’s not efficient to import everything.

Instead, we should import only the members that we need. We should write:

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

to import just the foo member which we need.

Photo by chuttersnap on Unsplash

Do Not Export Directly From an Import

We shouldn’t export directly from an import. This means that we shouldn’t use the as keyword to rename the export directly within the export to something else.

It’s clearer to separate them from their own line. For instance, we can write the following code to do that:

module.js

export const foo = 1;

bar.js

export { foo as bar } from "./module";

index.js

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

In the code above, we exported the foo member from module.js as bar by using the export statement.

We shouldn’t do that since it’s not clear that we’re both importing from module.js and exporting the member as bar at the same time.

Instead, we should write the following in bar.js :

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

This way, it’s clear that we’re both importing and exporting in one module.

Conclusion

Class methods should reference this if it’s an instance method or be static if they don’t.

We should use ES6 modules since it’s a JavaScript standard. Also, we should separate imports and exports and shouldn’t import everything from a module.

Categories
JavaScript Best Practices

JavaScript Best Practices — Arrows, Const, and Duplicate Members

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 confusing arrows, assigning to const constants, and duplicate members.

Arrow Functions That Can Be Confused with Comparisons

Arrow functions have the => fat arrow which may be confused with inequality comparison operators like <= or => for people that may not be completely familiar with JavaScript.

Therefore, we may want to make our code easier to understand for them by not using arrow functions that look like comparison expressions.

For instance, the following function may be confusing for some people:

const foo = a => 1;

We have the foo function that has a parameter a and returns 1.

However, some people may confuse this with:

const foo = a >= 1;

or:

const foo = a <= 1;

which compares if a if bigger than or equal to 1 or if a is less than or equal to 1 respectively.

Therefore, we may want to make our arrow function less confusing by wrapping the function body with curly braces or wrapping the function signature with parentheses.

For instance, we can rewrite the the foo function in the following way:

const foo = a => {
  return 1
};

The code above makes our function clear by indicating that we want to return the value 1.

We can also rewrite it as follows:

const foo = (a) => 1;

The parentheses make our code’s reader clear than a is a parameter and it’s not a variable that we want to compare with 1.

No Modifying Variables That are Declared Using const

In JavaScript, constants that are declared with const can’t be reassigned to a new value.

If we write something like the following code, then we’ll get an error:

const a = 1;
a = 2;

When we run the code above, we’ll get the error ‘Uncaught TypeError: Assignment to constant variable.’ in the console and the code will stop running.

Therefore, we should be mindful of not doing that. If we want a to be able to be reassigned to a different value, then we should declare it with let instead.

For instance, we instead write the following:

let a = 1;
a = 2;

This way, a is declared as a variable instead of a constant and thus it can be reassigned to a new value.

Other operators that do assignment operation like += , -= , *= , /= , and %= also won’t work with const constants.

For instance, we’ll get the same error if we write the following:

const a = 1;
a += 2;

Loop variables that are declared with const also can’t be reassigned to a different value. For instance, we’ll get an error if we write:

for (const a in [1, 2, 3]) {
  a = 1;
}

In the code above, we tried to reassign a to 1, which also won’t work.

Duplicate Member Name in Classes

We don’t want duplicate member names in classes. This is because it’s confusing which one is actually the one that’s kept.

For instance, we shouldn’t be writing code like this:

class Foo {
  bar() {
    console.log("foo");
  }

  bar() {
    console.log("bar");
  }
}

In the code above, we have 2 bar instance methods. The 2nd would be kept, so the first one is useless.

Therefore, when we call the bar method as follows:

const foo = new Foo();
foo.bar();

We’ll see 'bar' logged in the console log output.

Therefore, we should just keep the one we want to keep or rename one of them if we need both.

We can write something like the following:

class Foo {
  foo() {
    console.log("foo");
  }

  bar() {
    console.log("bar");
  }
}

Then we can call both instance methods and see the logged value of both in the console.

Conclusion

We may want to rewrite arrow functions that may be confused with comparison expressions.

To do that, we can put our function signature in parentheses or add curly braces to the function body.

We shouldn’t reassign const constants to another value. That’s why it’s a constant.

Also, we shouldn’t have multiple members with the same name in a class. That’s just useless and confusing since the one that’s defined later just overwrites the one we have above.

Categories
JavaScript Best Practices

JavaScript Best Practices — Generator Functions and Class Assignment

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 spacing around generator functions and assigning classes to other values.

Spacing Around the * in Generator Functions

Spacing around generator functions should be consistent. We usually define generator functions with the following spacing.

For instance, we can define one as follows:

function* foo() {
  yield 1;
}

A generator function is defined by the function* keyword. It denotes that the function is a generator function.

Then the rest of the first line has the same parts as for any other traditional function.

After the function* keyword, we have foo , which is the function name, then the parentheses, and then one space character, and then the opening curly brace.

The generation function returns a generator when it’s called, which we can use the spread operator or the for...of loop on.

For instance, we can use it as follows:

const arr = [...foo()];

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

In the first line, we used the spread operator to spread the generated returned by foo into an array, so we get 1 .

In the loop, we looped through the entries returned by the generator returned by calling foo and then log the value in the loop.

The yield keyword returns the item that’s returned from the generator.

We can also define a generator within an object. We can do this 2 ways. The first is using same function* keyword as follows:

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

In the code above, we have the obj object that has the foo property with the function* keyword to indicate that the function is a generator function.

The spacing is the same as in the standalone generator example we have previously except that our function doesn’t have a name.

We can also shorten this by replacing the function* keyword with * as follows:

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

The code above is the short version of the previous example. The * symbol is separated by a space character from the property name foo .

With both, we can call it as follows:

const arr = [...obj.foo()];

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

And we get the same result as the other examples.

We can also define generator methods inside a class. For instance, we can define it as follows:

class Foo {
  * foo() {
    yield 1;
  }
}

In the code above, we have the foo generator function. The syntax of the function definition is the same as the object shorthand version that we have in the previous example.

Then we can call it as follows:

const arr = [...new Foo().foo()];

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

We can also define a static generator method in the Foo class instead of an instance method as follows:

class Foo {
  static * foo() {
    yield 1;
  }
}

In the code above, we have one before and after the * symbol.

Then we can use it as follows:

const arr = [...Foo.foo()];

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

The spacing for the * is standard so we can just follow that to make our generator functions and methods readable.

Photo by Inspired Horizons Digital Marketing on Unsplash

Don’t Modify Variables of Class Declarations

In JavaScript, a class is nothing special. It’s just syntactic sugar for constructor functions.

Therefore, like any other function, it’s just a regular object. The class name is a regular variable name that can be assigned anything else.

It’s better that we don’t assign our class name to something else even though we can. This way, we keep our code easy to understand by nothing writing confusing code like assigning class names to another value, including classes.

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

class A { }
A = 0;

If we did that, then A would be 0 because we reassigned it to 0 on the 2nd line.

Instead, if we want to assign 0 to something, assign it to another variable. For example, we can write:

class A {}
const b = 0;

Conclusion

Generator functions can be defined in many ways. We should keep the spacing consistent and follow conventions to make them easy to read.

In JavaScript, classes are just functions, which are just objects assigned to a variable name. Therefore, we can assign the class name to another value, just like another variable. We should avoid doing that to reduce confusion in our code.