Categories
JavaScript Best Practices

JavaScript Best Practices — Spacing and Variables

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at how to format file source code for readability.

We also look at how variables should be declared.

Indentation of Continuation Lines

If our line continues from the previous line, then we indent at least 4 spaces so that we can distinguish the line from the previous line.

Spacing

We should think about spacing when we’re writing our code.

Some spaces are required and some are not. Even though they aren’t required, we may want to have them.

Vertical Whitespace

We should have a single blank line between consecutive methods in a class or object literal.

Method bodies should have line breaks between logic groups.

However, we shouldn’t have blank lines at the start or end of the function.

We may have an empty line before the first and last method in a class or object literal.

Horizontal Whitespace

Horizontal whitespace usage depends on location.

We should never have trailing whitespaces.

We should have whitespace to separate reserved words like if , for , or catch .

function and super don’t need a space between them and the opening parentheses.

else or catch are used to separate reserved words from a closing curly brace preceding it.

We should put a space before an opening curly brace.

Also, we shouldn’t have any space in the opening of a template string expression.

So we shouldn’t have:

`a $ {y} b`

Instead, we should write:

`a ${y} b`

Both sides of any binary or ternary expression should also have a space between operands.

Commas and semicolons should also be added.

Colon in the object literal should also have one space after it.

Both sides of double slash that start an end of line comment should also have space before it.

Open-block comment character and both sides of those characters also have spaces.

No Horizontal Alignment

We shouldn’t have horizontal alignment in our code.

They’re hard to maintain.

For instance, we can just write:

{
  foo: 1,
  longer: 2,
};

instead of:

{
  foo:    1,
  longer: 2,
};

Function Arguments

We should wrap function arguments so that each line width is 100 characters or less.

For instance, we can write:

doSomething(
  fooArgumentOne,
  fooArgumentTwo,
  fooArgumentThree
)

Grouping Parentheses

We may have parentheses to group expressions that may be misinterpreted without them.

However, they’re unnecessary for expressions that follow delete , typeof , void , return , throw , case , in , of , or yield .

Comments

We may want to put comments in some of our code.

Block Comments

We can add comments as blocks with /* ... */ for multiline comments.

For single-line comments, we start with // .

Parameter Name Comments

We should comment on the naming of parameters if they don’t convey their meaning clearly enough.

For instance, we can write:

foo(obviousParam, /* greeting= */ 'hello');

Language Features

JavaScript has many dubious or dangerous features that we should avoid.

We should avoid the bad ones as much as possible.

Variable Declarations

There are much better ways to declare variables than others.

We should use them as much as possible.

Photo by Brooke Lark on Unsplash

Use let or const

We should use let or const to declare variables.

This way, they’re block-scoped and won’t be available outside the block.

Unless a variable needs to be reassigned, const should be used.

var keyword must never be used.

One Variable Per Declaration

We should never have more than one variable per declaration.

Therefore, something like let a = 3, b = 2; should never be used.

Declare Variables When Needed

We should declare variables right before they’re needed.

This way, we don’t have to follow the usage of one variable for too many lines.

Initialize as Soon as Possible

We should initialize variables as soon as possible.

This way, we won’t forget about them and get variable not initialized or undefined errors.

Conclusion

We should group or expressions with parentheses to clarify how they’re called.

Indentation should be added to blocks.

Vertical and horizontal whitespaces are both important.

Variables should be declared only when they’re needed. And they should be declared with let or const .

Categories
JavaScript Best Practices

JavaScript Best Practices — Object Creation

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at how to create objects, including namespacing members with global objects, destructuring dependencies, and private members.

Object Creation

We can create JavaScript objects in a few ways.

We can either use object literals or constructor functions/classes.

However, there’re some patterns that are useful for creating objects in an orderly manner.

Namespace Pattern

We can separate JavaScript code into namespaces so that we don’t have so many global variables.

Namespaces let us avoid name collisions and reduce the need to prefix names.

For instance, we can write:

const APP = {};
APP.Foo = class {}
APP.Bar = class {}
APP.c = 1;
//...

The code above creates a global APP object which has multiple classes inside it.

They’re in their own object so that name collision is minimized compared to having them all at the top level.

Now that we have JavaScript modules, we can just use that instead.

But if we’re using old-fashioned scripts, we can use this pattern.

It’s a bit more to type. Also, the state of the object is propagated everywhere since it’s all under one global object.

Nested names also mean longer property resolutions lookups.

General Purpose Namespace Function

We can’t assume the object that we have been in the script.

Therefore, we may want to check if it’s there first before we put things in there.

For instance, we can write:

APP = APP || {};

In case APP isn’t defined yet, we should check with the || operator to see if it’s defined.

We can also create a namespacing function.

For instance, we can write:

const namespace = (path) => {
  const parts = path.split('.');
  const obj = {};
  let nestedObj = obj;
  for (const p of parts) {
    nestedObj[p] = {};
    nestedObj = nestedObj[p];

}
  return obj;
}

const obj = namespace('foo.bar');

The code above created a namespace function that takes an object path separated by dots.

It separates the path and creates the empty nested objects that we need.

Then we can put that in the function.

Declaring Dependencies

Most dependencies are namespaces, which we can use to reference global variables from dependencies easily.

For instance, we can write:

const {
  foo,
  bar
} = APP;

to destructure the APP namespace into its properties.

This way, we can access them individually in functions or any other piece of code.

Private Properties and Methods

JavaScript has no private properties and methods in objects and constructors.

To keep the private, we either keep them in modules or functions.

If we keep them in objects, anything can access them.

For instance, we can write:

const obj = {
  foo: 1,
  getFoo() {
    return this.foo;
  }
};

Then we can access the properties as follows:

obj.foo
obj.getFoo()

Nothing can stop us from accessing them.

Likewise, with constructors and classes, everything is public.

For instance, if we have:

class Foo {
  constructor() {
    this.foo = 1;
  }

  getFoo() {
    return this.foo;
  }
};

Then we can write:

(new Foo()).foo;

or:

(new Foo()).getFoo();

Private Members

If we want private members, then they can’t be a property of this .

So we can write:

class Foo {
  constructor() {
    const foo = 1;
  }

  getFoo() {
    return this.foo;
  }
};

We can only put them into functions and declare them with let and const to avoid exposing them to the public.

With the constructor function syntax, we can write:

function Foo() {
  const foo = 1;
};

Privileged Methods

Public members that have access to private members are called privileged methods.

We can define one as follows:

function Foo() {
  const foo = 1;
  this.getFoo = function() {
    return foo;
  }
};

getFoo is a privileged method as it can access foo .

This way, we can keep things private while being able to use them when needed.

Conclusion

We can use the namespace pattern to put members in a global object to avoid name collisions.

Also, there’re no private members in classes or constructors. So we’ve to define ordinary data with let or const to make them private.

Privileged methods are ones that can access private data.

Categories
JavaScript Best Practices

JavaScript Best Practices — Loops and Returns

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 writing loops and functions like loop length, nesting, and returns.

Make Our Loops Short Enough to View all at Once

Our loops should be short so that the whole thing can be viewed on the page.

Otherwise, it’s too long, and we should break it up.

Limit Nesting to 3 Levels

Nesting usually makes our code harder to read. Therefore, we can limit it to 3 levels max.

Other than that, it’s way too much and we should eliminate them by putting some code into functions for example.

Move Loop Innards of Long Loops into Functions

If we have a long loop, that means that we’ve to break our code into smaller functions.

Small functions and loops are easier to read, so we should write those instead of putting everything into one big loop.

Make Long Loops Especially Clear

If we can’t avoid long loops, then we should make them as clear as possible.

Otherwise, we’ll have a hard time reading our own code.

Correspondence Between Loops and Arrays

Loops and arrays are often related. Many loops are used for manipulating arrays.

Loop counter variables so one to one with array indexes.

However, with JavaScript, we don’t always need to write loops to do array operations.

For instance, there’re many methods in the Array constructor that we can use to create loops with.

We can use map to map each array entry to new values by passing in a callback into it.

For instance, we can use it to square all numbers in an array as follows:

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

There’s also the filter method for returning an array with entries that meet some given condition.

We can find all the even numbers in an array as follows:

const arr = [1, 2, 3];
const result = arr.filter(a => a % 2 === 0);

Also, to search for an array item, we can use the find method as follows:

const arr = [1, 2, 3];
const result = arr.find(a => a % 2 === 0);

The code above will find the first even number in the array.

There’re many more methods that we can use instead of writing out loops.

We may want to consider those before using loops.

Multiple Returns from a Function

In JavaScript, we don’t have to exit a function at the end of a function.

We can use the return keyword to stop the function.

For instance, we can write something like:

const fn = () => {
  if (invalid) {
    return;
  }
  //...
  return {
    //...
  }
}

We can stop the function when we have invalid set to true in the example above.

This lets us clean up our code by reducing nesting.

Use a return When it Enhances Readability

We can add return statements to enhance readability.

It’s useful for stopping a function when certain conditions are met as we have seen.

Use Guard Clauses to Simplify Complex Error Processing

As we can see from the example above, we can return early.

A group of code that lets us stop running a function early is called a guard clause.

For instance, in the function we have above:

const fn = () => {
  if (invalid) {
    return;
  }
  //...
  return {
    //...
  }
}

The following is the guard clause:

if (invalid) {
  return;
}

It’s much better than writing things like:

const fn = () => {
  if (!invalid) {
    //...
    return {
      //...
    }
  }
}

which does the same thing but has a lot more nesting.

Nesting is hard to read and the guard clause lets us avoid nesting.

Minimize the Number of returns in Each Function

Guard clauses are great, but we shouldn’t use them too frequently.

The more return statements we have in our code, the harder it is to understand.

Conclusion

Nesting is bad in loops or functions, so we should reduce them.

In functions, we can reduce nesting with return statements. We can return a function before the last line in some cases.

Instead of loops, we can also use array methods to do array operations.

Categories
JavaScript Best Practices

JavaScript Best Practices — Commenting Control Structures and 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 the best practices for commenting on control structures and functions.

Commenting Control Structures

Space before the control structure is a good place for comments.

We may want to explain why we’re doing something with the loop or conditional statement.

Put a Comment Before Each if, case, loop, or Block of Statements

If we want to put in comments, we should put them before if , case , loops or blocks.

It’s a good place to clarify those structures.

Comment the End of Each Control Structure

We may also want to put them at the end of each control structure.

Treat End-of-Loop Comments as a Warning Indicating Complicated Code

Our loops usually don’t need much explanation.

If we feel like that we’ve to explain it with comments, then it’s probably too complex.

Therefore, we should simplify them in those cases.

Commenting Functions

We don’t need long comments before our functions.

If we have to write to them for every function, then people won’t be willing to create more functions, and rather make the existing ones long and hard to read.

Keep Comments Close to the Code they Describe

We should keep comments close to the code they describe so that they’ll more likely be maintained.

Also, we know what it’s actually commenting on if they’re close to the code that they’re describing.

Describe Each Function in One or Two Sentences at the Top of the Function

One or 2 sentences are enough to describe a function.

If we can’t describe our function in one or 2 sentences, then it’s probably too long.

Except for get and set accessor functions, the rest may use some description.

Document Parameters Where they are Declared

When parameters are declared, then we may want to describe them.

This way, we know what they’re storing if needed.

Comment on the Function’s Limitations

If there’re any limitations that we have to know about, then we should document them so that we know what we may run into when calling it.

Document the Function’s Global Effects

If a function changes global variables, then we should know about that.

It might be hard to find what we’re doing with global variables, so comments may help with that.

Document the Source of Algorithms that are Used

If we used an algorithm from some other source, then we may want to attribute it to that source so that people can look them up in that source.

Use Comments to Mark Parts of Our Program

We may also want to mark parts of our program with comments so we can find them later.

Describe the Design Approach to the Class

If we design a class, then we may want to explain our design approach by leaving some comments in the class.

The design philosophy, ap[approach, and alternatives that were discarded can all be explained.

Describe Limitations and Usage Assumptions

Any limitations and usage assumptions with our class can be explained with comments.

Issues with error handling, global effects, and uses of algorithms can all be explained.

Comments on Files

The top of code files is also good place to put comments.

Describe the Purpose and Contents of Each File

The purpose and content of each file may also use some explanation.

For instance, we can explain why there are multiple classes in a file.

Also, we may also want to explain why we divide our code into files in the way that we did.

Photo by Kon Karampelas on Unsplash

Put Name, Email Address, and Phone Number in Block Comments

Name of the author, his or her email address, and phone numbers may be in comments.

However, the name of the author may already be in the source control history, so we may not need it in the comments.

Include a Version-Control Tag

To identify the version of the program built, we may want to add a version control tag automatically to make that easier.

Include Legal Notices in the Block Comment

If a legal notice is required, we may also want to include in a comment in our code files.

Conclusion

There’re a lot of things that we may want to write comments for.

Legal notices, version numbers, design approaches and more can be outlined with comments.

We may also explain the limitations of our code in there.

Categories
JavaScript Best Practices

JavaScript Best Practices — Bundles, Linting, and Objects

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some best practices when developing in JavaScript, including bundling, linting, and object creation.

Minify in Production

Before releasing our code to production, we got to minify it so that users won’t have to spend too much time downloading our JS app.

Frameworks have built-in programs to bundle and minify our code.

Also, we can create our own bundler with packages like Grunt and some plugins.

Webpack and Parcel are also popular module bundlers.

Bundling strips white spaces, newlines, and comments.

They also rename variables to shorter names when it’s safe to do so.

Minifiers can only rename local variables, so that’s another reason we shouldn’t use global variables.

We should let the minifier do the minification.

Our code should have the descriptive names and let the minifiers strip characters as they see fit.

Run ESLint

ESLint checks for syntax errors and bad practices and either tell us to correct it or it can correct some of them automatically.

For instance, it’ll detect unreachable code, any old syntax that can be replaced with better new syntax, etc.

It’ll also detect bad constructs like eval , with , and void , which should never use.

Improperly escaped characters in regexes will also be detected to prevent potential security issues.

It’s available as a Node package and frameworks tools probably already have them included.

Therefore, we should use them to catch anything that we shouldn’t have.

Object Literal

Object literals are a simple way to define objects in JavaScript.

We can define objects in a one-off manner.

They contain key-value pairs, where the value may also be an object.

For instance, we can define an object literal as follows:

const cat = {};
cat.name = "joe";

or we can write:

const cat = {
  name: "joe"
};

We can also add methods to an object as follows:

const cat = {
  name: "joe",
  getName() {
    return cat.name;
  }
};

We can use the delete operator to remove a property.

For instance, we can write:

delete cat.name;

An object literal will have inherited properties from the Object prototype.

Therefore, even an empty object isn’t actually empty.

To write an object literal, we start with curly braces and delimit each property with commas.

Property names and values should be separated with a colon.

Objects from a Constructor

We can also create objects from a constructor.

Constructors are functions that have their own instance variables and prototype.

For instance, we can create a new Date instance by writing:

const date = new Date();

We used the new operator on the constructor to make it return a new Date instance.

Object Constructor

The Object constructor isn’t very useful for us.

It’s just a long way to create an object.

Also, we can pass in whatever value we want to create an object of our choice, which is confusing.

If we pass in a string, we get a string returned. So if we have:

const str = new Object("foo");

we get that str is a string.

It has access to all the string methods, but the type according to typeof is 'object' .

That’s no good since it’s confusing.

Therefore, we should just stick to literals as much as possible.

Otherwise, we’ll be confused about the type and the content.

Photo by Jerry Wang on Unsplash

Custom Constructor Functions and Classes

We can define a constructor function as follows:

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

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

Then we can write using the new operator to invoke it to create a new Person instance as follows:

const person = new Person('joe');
person.greet();

When we use the new operator, an empty object is created and referenced by this inheriting the prototype of the constructor.

Properties and methods are added to the empty object.

The created object referenced by this is returned.

We can also rewrite that with the class syntax by writing:

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

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

Both are exactly the same. Just that the members are in different places.

Conclusion

Minifying is a must in production so that users won’t have to download as much data.

Also, linting helps us catch syntax errors and bad practices.

Finally, classes and constructors are the same in JavaScript.