Categories
JavaScript Best Practices

Adopting Better JavaScript Code Style — Basic Syntax and Async

The way we write JavaScript can always be improved.

In this article, we’ll look at how to improve our code styles by improving the basic syntax we use in our code.

Naming Conventions

The names we use for identifiers like function names, class names, variable names, etc. should follow commonly accepted conventions.

They’re accepted because they make our code clear for most people.

To write good names, we should avoid single-letter names. We should be descriptive with our naming.

For instance, the following is bad:

let x;

because no one knows what x means.

However, the following is good:

let numFruits;

because we know from the variable name that it’ll store the number of fruits.

We also know that it’s a number, which is even better since JavaScript doesn’t have any data type annotations built-in to identify the type of a variable.

Likewise, the following is a bad way to name a function:

const q = () => {};

but the following is good:

const countFruits = () => {};

With the function name above, we actually know what the function actually does.

Notice that the good naming examples all use camelCase. This is the way to name most things, with the exception of class and constructor names.

For class and constructor function names, we should use PascalCase to name them.

For instance, the following is a good example of a class name:

class Fruit {}

for constructor functions, we should name it as follows:

function Fruit(){}

Semicolons

We should always add semicolons at the end of a new line. Because if we don’t the JavaScript interpreter will automatically add them to any place that it sees fit.

For instance, if we have something like:

const foo = ()=> {
  return
    'foo'
}

Then when we call foo , it’ll return undefined because the function ended after the return keyword. The 'foo' string isn’t considered to be part of the return statement.

Therefore, we should always add semicolons as follows:

const foo = () => {
  return 'foo';
}

Now the JavaScript Interpret knows that return 'foo'; is actually one line.

It also makes human reading it understand it better as well as it removed any ambiguity.

Comments

If we need comments, we need to standardize our comment schemes so that they’re consistent.

For a block comment, we write the following:

/* comment */

To make documentation easy, we should use JSDoc comments so that we can use them to create documentation for us automatically without doing extra manual work:

/**
 * Represents a fruit.
 * @constructor
 * @param {string} name - The name of a fruit.
 * @param {string} color - The color of a fruit.
 */
function Fruit(name, color) {
  this.name = name;
  this.color = color;
}

The code above has a big block comment with the parameters of the Fruit constructor function with the name and color parameters.

In the comment, we annotate the data type of the parameters so that it’s clear to everyone reading the code comment on what the function accepts.

Since JavaScript has no way to distinguish the data type of the parameters from within the code, the type annotation in the comments is useful.

The comment above is standard a JSDoc style comment.

Promises

If we deal with asynchronous code that deals with callbacks, then we should wrap them in a promise so that we can chain them easily.

For instance, if we need to run code with a delay, we should write a function that returns a promise to do that:

const delay = (ms) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('delay');
      resolve();
    }, ms)
  })
}

In the code above, we have a delay function that returns a promise that runs the serTimeout function inside it to run some code.

Then within the setTimeout callback, we call resolve to fulfill the promise.

This way, we won’t get callback hell by nesting async callbacks many levels deep.

To handle errors, we can write the following code:

const fs = require('fs');

const readFile = (filename) => {
  return new Promise((resolve, reject) => {
    fs.readFile(filename,(err, data) => {
      if (err) {
        reject(err);
      }
      resolve(data);
    });
  })
}

(async () => {
  const data = await readFile('foo.txt');
  console.log(data.toString())
})();

In the code above, we have the readFile function, which returns a promise that runs the fs.readFile function. Inside the readFile ‘s callback, if err is defined, then we call reject to reject the promise. We can resolve the promise if data is provided.

Then we can use async and await to use the promise as we did in the last few lines of code.

Conclusion

Names and comments should be consistent. Names should be self-documenting with descriptive names and in the case that is commonly accepted.

Also, we should wrap async code that aren’t promises with promises so that we can chain them.

Categories
JavaScript Best Practices

Ways to Write Better JavaScript — Use Modern JavaScript Features

The way we write JavaScript can always be improved. As the language evolves and more convenient features are added, we can also improve by using new features that are useful.

In this article, we’ll look at some modern JavaScript features that we should use to make our lives easier.

**async** and**await**

async and await lets us chain promises in a shorter way. For instance, we can write:

(async () => {
  const res = await fetch('https://api.agify.io/?name=michael');
  const data = await res.json();
  console.log(data);
})();

instead of:

fetch('https://api.agify.io/?name=michael')
  .then(res => res.json())
  .then(data => console.log(data))

With async and await , we eliminated the need to call then and return promises in callbacks.

This is much better than nesting anything. This makes our code easier to read and write.

To catch errors with async and await , we can use try...catch with async function as follows:

(async () => {
  try {
    const res = await fetch('https://api.agify.io/?name=michael');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
})();

We just use the existing try...catch syntax to catch errors.

With the old catch method, it’s a bit longer:

fetch('https://api.agify.io/?name=michael')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.log(err))

Then catch will catch the error and display the content.

The 2 are equivalent. It’s just that async and await are shorter than the old then and catch methods.

**let** and**const**

let and const are must-use when we need to declare variables or constants respectively.

They completely replace var and does a much better job than var for declaring variables.

let and const are both block-scoped, and the variables and constants declared with them can only be used after they’re declared.

On the other hand, var is function scoped and the variable declaration itself is hoisted to the top of the code without the value.

var causes so much confusion that they all should be replaced with let and const .

const should also be used so that values declared with them can’t be reassigned. They also must have an initial value assigned to them immediately when it’s declared.

Probably most entities should be declared with const since most of them shouldn’t be reassigned to a different value.

Note that values declared with const are still mutable, they just can’t be reassigned.

For instance, if const is used to declare an array, we can call push to add more items to the array.

Object properties can also be assigned to different values.

Arrow Functions

Arrow functions are another must-use modern JavaScript feature. It’s shorter and also we don’t have to worry about the value of this inside an arrow function.

Legacy constructs like the arguments object also can’t be used within an arrow function, which is good.

We can use it to declare anonymous functions for callback or we can assign them to a variable or constant.

For instance, we can write:

fetch('https://api.agify.io/?name=michael')
  .then(res => res.json())
  .then(data => console.log(data))

Then we have arrow functions in the then callback which returns promises in the first then callback and calls console.log in the 2nd then callback.

As we can see, we returned something without using the return keyword in single-line arrow functions.

This is also much shorter than using the traditional functions since we don’t have to type out the function keyword.

However, if we have a multiline arrow function, then we have to use the return keyword and parentheses to return a value.

For instance, if we have a long function, we write something like:

const sum = (...nums) => {
  return nums.reduce((a, b) => a + b, 0)
}

Spread Operator

The spread operator lets us copy and combine objects in ways that are hard to do without it.

For instance, with objects, we can write:

const obj1 = {
  a: 1
};
const obj2 = {
  b: 2
};
const merged = {
  ...obj1,
  ...obj2
}

The merged is {a: 1, b: 2} since we spread the entries from obj1 and obj2 into a new object.

The only other way is to use the Object.assign method, which we use as follows:

const obj1 = {
  a: 1
};
const obj2 = {
  b: 2
};
const merged = Object.assign({}, obj1, obj2);

We can also use it to merge arrays into one. We can use it as follows:

const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2];

The code above will spread arr1 and arr2 ‘s entries into the merged array.

And so merged is [1, 2, 3, 4] .

Conclusion

Using modern JavaScript features is a must for any JavaScript app to keep the code clean and short.

Some useful features include the spread operator, arrow functions, let and const , and async and await .

Categories
JavaScript Best Practices

Ways to Write Better JavaScript — Use TypeScript

The way we write JavaScript can always be improved. As the language evolves and more convenient features are added, we can also improve by using new features that are useful.

In this article, we’ll look at some ways to write better JavaScript by using TypeScript.

Use TypeScript

TypeScript is a natural extension to JavaScript. It lets us write JavaScript code that’s type-safe. Therefore, we can use it to prevent lots of data type errors that would otherwise occur if we didn’t use TypeScript.

Also, it provides autocomplete for things that otherwise wouldn’t have the autocomplete feature like many libraries. They use TypeScript type definitions to provide autocomplete for text editors and IDEs to make our lives easier.

TypeScript doesn’t turn JavaScript into a different language. All it does is add type checking to JavaScript by various type-checking features.

Therefore, all the knowledge that is used for JavaScript all apply to TypeScript.

For instance, we can create a function with TypeScript type annotations as follows:

const foo = (num: number): number => {
  return num + 1;
}

In the code above, we have the foo function with a num parameter that’s set to the type number . We also set the return type to number by specifying the type after the : .

Then if we call the function with a number, the TypeScript compiler will accept the code.

Otherwise, it’ll reject the code and won’t build the code. This is good because JavaScript doesn’t stop this from happening.

Interfaces

TypeScript provides us interfaces so that we know the structure of an object without logging the object or checking the value otherwise.

For instance, we can create one as follows:

interface Person {
    name: string;
    age: number;
}

Then we can use it as follows:

const person: Person = { name: 'jane', age: 10 }

If we miss any of these properties, then we’ll get an error as the TypeScript compiler is looking for them.

We can also use it to enforce a class implementation as follows:

interface PersonInterface {
    name: string;
    age: number;
}

class Person implements PersonInterface {
    name: string;
    age: number;
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

In the code above, we have both the name and age fields. If we skip any of them, then we’ll get an error from the TypeScript compiler.

If we want to embrace the dynamic typing nature of JavaScript, we can add dynamic index signatures to JavaScript. Also, there’re union and intersection types to combine different types into one.

For instance, we can use it as follows:

interface PersonInterface {
    name: string;
    age: number;
    [key: string]: any;
}

In the code above, we have:

[key: string]: any;

to allow dynamic keys in anything that implements PersonInterface that has anything as a value.

Then we can have any property in addition to name and age in any class that implements PersonInterface or an object that’s cast to the PersonInterface type.

Union types let us join different types together. For instance, we can use it as follows:

interface Person {
    name: string;
    age: number;
}

interface Employee {
    employeeId: string;
}

const staff: Person | Employee = {
    name: 'jane',
    age: 10,
    employeeId: 'abc'
}

In the code above, the | is the union type operator. It lets us combine both the keys from both interfaces into one without creating a new type.

Another good thing about TypeScript is nullable properties. We can make properties optional with the ? operator.

For instance, we can use the following code:

interface Person {
    name: string;
    age?: number;
}

With the ? operator, we made age an optional property.

typeof Operator

Another great feature of TypeScript is the typeof operator, which lets us specify that something has the same type as something else.

For instance, we can use it as follows:

const person = {
    name: 'jane',
    age: 10,
}

const person2: typeof person = {
    name: 'john',
    age: 11,
}

In the code above, we have the person2 object, which has the same type as person since we specified that with typeof person . Then person2 must have the name and age properties or we’ll get an error.

As we can see, we don’t need to specify any interfaces or classes explicitly to specify types. This is handy for getting the types of imported libraries that don’t come with type definitions.

Conclusion

With TypeScript, we made refactoring easy since it’s harder to break the existing code with the type and structure checks that it provides.

It also makes communication easier because we know the type and structure of our objects, classes, and return value of functions.

Categories
JavaScript Best Practices

More Bad Smells in Code

Writing software is hard. There’re only a few ways to do it right and there’re many ways to do it wrong and make our lives hard.

In this article, we’ll look at some ways to write bad code by looking at a few codes smells.

Primitive Obsession

Programming languages like JavaScript have 2 kinds of data. They’re primitive values and reference values.

Primitive values are the building blocks of our program. They store things like numbers and strings.

Reference types come in the form of objects. They store multiple primitive values and other objects all under one umbrella.

Objects ultimately have primitive values at the bottom of the object’s tree structure.

If we have a bunch of primitive values that are always together, then we can have an object to put them all in one package.

This way, we don’t have to deal with so many primitive values everywhere.

Switch Statements

Switch statements are conditional statements tat contains many cases indie them.

The problem is that there’re lots of duplication in switch statements.

If we have them scattered in different places, then we’ve to update our different switch statements so that we can change our code.

In this case, we should consider polymorphism instead of using switch statements.

Parallel Inheritance Hierarchies

This occurs when an inheritance tree depends on another tree by composition.

They maintain a relationship where one subclass of a dependent inheritance tree depends on a subclass of another inheritance tree.

This isn’t good since if we have to make a subclass of a class, then we’ve to make a subclass of another.

Therefore, we’re forced to duplicate the inheritance hierarchies when making the 2 classes.

We can eliminate this by moving the instance of one hierarchy refers to the instance of another rather than duplicating the inheritance tree for both classes.

Lazy Class

A lazy class is where we create a class that doesn’t have much in it.

In this case, we should remove it since it cost time and money to maintain an extra class.

Speculative Generality

If we don’t need anything now, then we shouldn’t add it.

Writing flexible code makes our code more complex and that will slow us down.

We should only add code that we need now and make it easy to change later.,

Therefore, we shouldn’t overengineer anything. If we have extra code that isn’t doing much now, then we should remove them.

We can spot them when those pieces of code are referenced by test cases. Then we know that they don’t provide us with much value.

Temporary Field

Temporary fields are ones that are only set in certain cases.

This makes the code hard to understand because we expected an object to need all of its variables.

If we need them, then we can extract them to their own class, so we only reference them when we need them.

Message Chains

Sending a chain of messages around our system isn’t great. We have a message having when something asks for an object and that object asks for another object and so on.

We may be calling a chain of getters or referencing a chain of variables.

In either case, the code is too coupled together with the implementation.

And it’s hard to navigate through the message chain for us to understand the code easily.

We should combine the chain into one so that we don’t have to so many objects.

Middle Man

Encapsulation is important. Therefore, we don’t want all our classes to know the details of another class.

However, we may go too far by introducing a middle man class that is only used to delegate calls to another class.

In this case, we can eliminate the middle man class and just call the code we need directly.

Inappropriate Intimacy

Classes can become too intimate and they know too many details of another class.

This isn’t good because too much knowledge means tight coupling. This means that when we change things in one class, we’ll likely break the code in the other class.

We should break them by removing the intimate code. Put any common code into their own class.

And we should avoid referencing any code that has the implementation details.

Subclasses may also know more than the parent class wants them to know. Un this case, we can hide those details by making them private.

Conclusion

We shouldn’t be obsessed with using primitive values. Use objects to group them when necessary.

Also, classes shouldn’t have too much knowledge of each other. We need encapsulation to losses coupling.

Parallel inheritance hierarchies are bad since they duplicate the inheritance tree.

Categories
JavaScript Best Practices

Bad Smells in Code

Writing software is hard. There’re only a few ways to do it right and there’re many ways to do it wrong and make our lives hard.

In this article, we’ll look at some ways to write bad code by looking at a few codes smells.

Duplicated Code

Duplicated code is almost always bad. If we see the same code structure in multiple places, then we’ve to change it by unifying them and then referencing that piece of code.

For instance, if we have 2 expressions in 2 methods of the same class, then we can put the duplicate expressions in their own method.

When we have 2 expressions in 2 sibling subclasses, then we can put the duplicated expressions in the superclass.

If we have duplicated code in 2 unrelated classes, then we can put them into their own class or top-level function if the language allows us to write standalone functions.

Long Method

We can divide a long method into smaller methods that do one thing.

This way, we can read each method much more easily and know what they’re doing.

The longer a procedure, the harder it is for us to understand. Therefore, we should decompose them into smaller methods that are a few lines long.

We can also clean up our code by replacing long expressions with shorter ones.

For instance, we can replace JavaScript loops with array methods that do the same thing.

Large Class

A large class is trying to do too much. It may have lots of instance variables and methods.

Therefore, we should reduce them by splitting one big class into smaller classes that do the same thing.

We can extract the big class into smaller classes that do one thing only.

This way, we keep things easier to read and understand.

If we’re writing web apps, then we need to separate presentation logic from the business logic, for example.

Then we have 2 pieces of code that do their own thing. We also have to keep them loosely coupled by not referencing too much of each other’s implementation.

Long Parameter List

Long lists of parameters in functions need to be reduced. If we have more than 5, then we’ve to find ways to reduce them.

The fewer parameters we have, the better the function is.

More parameters mean it’s easier to make mistakes when passing them in,

One way to do that is to put everything in an object, then we don’t have to pass different things in as separate parameters.

Divergent Change

If we have multiple classes that change in different ways but share the same code, we can move the common code between them to one location and then reference them there.

This way, only the code that’s different is are in their own place and the common code stays in one place.

Shotgun Surgery

Shotgun surgery is changing different code in different classes. They’re then all hard to find and it’s easy to miss an important change.

We can deal with this by moving the shared code to their own location and we can make the change to that part all at once so that we don’t have to make small changes everywhere.

Feature Envy

This is a code smell where we get all data from many different places and put them together to get some results.

This makes coupling between classes tight and makes any changes risky because of that.

Instead of referencing many parts of our code, we should instead reduce the coupling by creating a method to reference all that code in the originating class and then use that method to get the result that we want.

The data is usually all in one class, but we just didn’t both to access them in a loosely coupled way.

Data Clumps

We may often see the same few pieces of data together in many different places.

If the clumps of data appear as fields, then we can turn those clumps of data into its own object so that we can just reference them anywhere.

If the clumps of data are passed in as arguments to functions, then we can combine them to an object parameter to avoid passing them in as multiple arguments.

Instead, we have one object argument that we can use anywhere/

Conclusion

As we can see, there’re many ways to write code in messy ways.

Duplicate code should be combined. Multiple clumps of data should be combined into one so that we can use them together.

Tightly coupled should be mostly decoupled to reduce risks of breaking things.