Categories
JavaScript Best Practices

JavaScript Best Practices — Blocks and Lines

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 layout code with blocks and lines.

Pure Blocks

JavaScript uses curly braces to delimit blocks.

We can use them to denote functions, loops, and standalone blocks.

For instance, we can write:

while (x < 10) {
  //...
}

to define a while loop.

To define a function, we can write:

const foo = (x) => {
  //..
}

We can write conditional statements as follows:

if (val == 1) {
  //..
}

or:

switch (val) {
  case 1: {
    //...
    break;
  }
  //...
}

To define standalone blocks, we can write:

{
  let x = 1;
}

Whatever block-scoped variables and constants are only available inside the block.

With braces, we can define blocks easily as braces denote block boundaries.

Endline Layout

For single-line body if blocks, we can write the following:

if (x === 1)
  doSomething()

However, we should just put braces around the block so that we can distinguish the block easily.

Laying Out Control Structures

We should take note of a few things when we’re laying out control structures.

Avoid Unindented Begin-End Pairs

We can write the following for loops and conditional:

for (let i = 0; i < MAX_LINES; i++) {
  //...
}

and:

if (i === 0) {
  //...
}

In the code above, we have the opening curly brace on the first line so that they’re associated with the loop and conditional rather than staying separate on their own line.

Avoid Double Indentation with Begin and End

We shouldn’t write something like:

for (let i = 0; i < MAX_LINES; i++)
  {
    //...
  }

since the braces are part of the loop rather than subordinate to it, we shouldn’t write like that.

Use Blank Lines Between Paragraphs

We should separate groups of code with blank lines.

This way, we should which lines of code are related to each other.

Blank lines also open up natural spaces for comments.

Format Single-Statement Blocks Consistently

A single statement block is a statement followed by a control structure.

A statement followed by an if test is one example of this.

We should just treat them like any other blocks by putting in curly braces and indentation as follows:

if (i === 0) {
  doSomething();
}

For Complicated Expressions, Put Separate Conditions on Separate Lines

If we have complex expressions, then we should put the parts in their own line.

For instance, if we have:

str.incluees('a') && str.incluees('z') && str.incluees(0) && str.incluees(9)

Then we should put each check onto their own line:

str.incluees('a') &&
str.incluees('z') &&
str.incluees(0) &&
str.incluees(9)

Now it’s much easier to read.

No End Line Exception for Case Statements

We should format case statements by indenting with 2 spaces like any other blocks.

For instance, we should write:

switch (color) {
  case 'red':
    //...
    break;
  case 'green':
    //...
    break;
  case 'blue':
    //...
    break;
  default:
    //...
    break;
}

This way, we keep the spacing the same other kinds of code.

Statement Length

Statements should be short enough so that we don’t have to scroll horizontally to read the whole thing.

80 characters maximum is an ideal length since that fits on most screens.

The limit also discourages deep nesting.

Using Spaces for Clarity

Spaces are great for clarity. Therefore we should use them.

Use Spaces to Make Logical Expressions Readable

Spaces should be used to make logical expressions more readable. For instance, instead of writing:

a<1 && a<10

we should write:

a < 1 && a < 10

Having extra space is a lot better than without them.

Use Spaces to Function Arguments Readable

When we call functions, we should separate the arguments with spaces, so that we can read them more easily.

For example, instead of writing:

add(1,2,3)

We write:

add(1, 2, 3)

Conclusion

To keep our code easy to read, we should make sure that our blocks are formatted with blocks and indentation.

Also, we should keep related code on the same line or group.

Spaces between arguments and operands also make our code much clearer.

Categories
JavaScript Best Practices

JavaScript Best Practices — Variables, Arrays, and Objects

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.

Also, we should make our lives easier by making the best use of recent JavaScript features.

Array Literals

We should be careful when we create array literals.

The spacing and indentation should be consistent.

Use Trailing Commas

Trailing commas should be added so that we can rearrange them easier.

For instance, we should write:

const values = [
  'first',
  'second',
];

Don’t Use the Array Constructor

We should never use the Array constructor.

It acts differently when we pass in one argument and we pass in more than one argument.

If we have one argument, then it returns an array with the number of empty slots.

If there’s more than one argument, then it returns an array with the arguments in it.

Instead, we should use array literals.

For instance, we write:

const arr = [1, 2, 3];

instead of:

const arr = new Array();

or:

const arr = new Array(1);

or:

const arr = new Array(1, 2, 3);

Non-Numeric Properties

We shouldn’t have non-numeric properties ion an array other than length .

Instead, we should use a Map on an object.

Arrays aren’t meant for storing key-value pairs.

Destructuring

We can use arrays on the left-hand side to destructure the entries.

Also, a final rest element may be included.

For instance, we can write:

const [a, b, c, ...rest] = getArray();

or:

const [, ,a, b] = getArray();

Spread Operator

The spread operator can be used to make shallow copies of arrays or merged entries from multiple arrays into one.

For instance, we can write:

const copy = [...foo];

instead of:

const copy = Array.prototype.slice.call(foo);

And:

const merged = [...foo, ...bar];

instead of:

const merged = foo.concat(bar);

Object Literals

There are things to consider when we’re defining and using object literals.

Use Trailing Commas

We should use trailing commas and a line break between the final property and the closing brace.

For instance, we can write:

const foo = {
  a: 1,
  b: 2,
}

This way, each key-value pair is consistent and we can rearrange them easier.

Don’t Use the Object Constructor

We shouldn’t use the Object constructor.

We just have to write extra code to use the Object constructor to create objects without extra benefits.

Instead, we should use object literals.

For instance, we can write:

const foo = {
  a: 1,
  b: 2,
}

Don’t Mix Quotes and Unquoted Keys

We shouldn’t mix quoted and unquoted keys unless.

For instance, we shouldn’t write:

{
  height: 2,
  'maxHeight': 43,
}

Instead, we write:

{
  'height': 2,
  'maxHeight': 43,
}

or:

{
  height: 2,
  maxHeight: 43,
}

Method Shorthand

We should use the method shorthand in classes or objects instead of the function keyword.

They do the same thing.

For instance, instead of writing:

const foo = {
  bar: function() {
    return 'bar;;
  },
};

We write:

const foo = {
  bar() {
    return 'bar;;
  },
};

Likewise, we can do the same with class methods, we write:

class Foo {
  bar() {
    //...
  }
}

Shorthand Properties

We can use shorthand properties in object literals.

For instance, we write:

const foo = 1;
const bar = 2;
const obj = {
  foo,
  bar,
};

Object Destructuring

Like arrays, we can destructure objects.

For instance, we can write:

function foo(bar, {
  num,
  str = 'some default'
} = {}) {
  //...
}

This way, we separate the 2nd argument, which should be an object into separate variables.

However, we shouldn’t nest destructured variables too deeply.

For instance, we may want to think twice if we write:

function foo(bar, {
  num,
  baz: {
    str = 'some default'
  }
} = {}) {
  //...
}

The more nesting is our code, the harder it is to read.

Enums

We can create constant objects that act like enums.

To do that, we have upper case keys in an object. The object should be assigned to a const variable.

Also, the variable name should be PascalCase.

For instance, we can write:

const LengthUnit = {
  METER: 'meter',
  FEET: 'feet',
};

Conclusion

We should use new features in JavaScript to make our lives easier.

They include destructuring, let and const .

Also, we should define arrays and objects with array and object literals respectively as much as possible.

Categories
JavaScript Best Practices

JavaScript Best Practices —Recursion and Booleans

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 unusual control structures and boolean expressions in our code.

Recursion

Recursion is when a function calls itself. We may want to do that to do operations on tree structures.

We can also use them in place of loops.

When we write recursive code, we’ve to consider a few things.

Make Sure the Recursion Stops

Infinite recursion will probably crash our program. Therefore, we should make sure that recursion ends.

We should make sure that our recursion code has a base case.

Use Safety Counters to Prevent Infinite Recursion

If our recursive code doesn’t have a clear base case, we should add a safety counter to our code to stop infinite recursion.

For instance, we can write it as follows:

const fn = (safetyCounter = 0) => {
  if (safetyCount === 100) {
    return;
  }
  //...
  fn(safetyCounter + 1);
}

We increment safetyCounter by 1 each time we call fn until it reaches 100, then it stops.

Limit Recursion to One Function

We should never write code that is cyclically recursive. So code where A calls B, B calls C, and C calls A isn’t good.

They’re hard to detect and we can run into problems with infinite recursion easily.

Therefore, we should just have a function call itself and nothing else.

Keep an Eye on the Stack

The call stack is where all the functions that are called are listed in one collection in LIFO order.

We got to make sure that we don’t overflow that with our recursive code.

A safety counter makes sure that we don’t overflow the stack.

Don’t Use Recursion for Factorials or Fibonacci Numbers

Recursion for factorials or Fibonacci numbers is slower than with iteration.

Also, the memory use of these operations required for computing Fibonacci numbers and factorials are unpredictable.

It’s also a lot harder to understand than the iterative version.

Table-Driven Methods

Table driven method is a schema that lets us look up information is a table rather than logic structures.

We can use that to rewrite logic in a much easier way in situations we can look up one thing from another.

To do this with JavaScript, we can use a map or an object.

For instance, we can write the following to look up the day of the week by a number:

const dayMap = {
  0: 'Sunday',
  1: 'Monday',
  2: 'Tuesday',
  3: 'Wednesday',
  4: 'Thursday',
  5: 'Friday',
  6: 'Saturday',
}

This lets us look up the day of the week by writing:

dayMap[1]

to get Monday.

Using true and false for Boolean Tests

If we do boolean tests, we should use true and false to compare our boolean expressions.

Most people won’t know other values are opposites better than true and false .

Since JavaScript has a boolean type, we can use that easily.

Compare Boolean Values to true and false Implicitly

We can compare booleans without writing out true or false .

If it’s not written out, then we compare it against true . For instance, if we have:

if (numApples > 1) {
  //...
}

that’s the same as:

if (numApples > 1 === true) {
  //...
}

Break Complicated Tests into Partial Tests with New Boolean Variables

We shouldn’t create long boolean expressions for checks.

If we have to check many conditions, then we should assign some expressions to their own variables and use those.

Move Complicated Expressions into Boolean Functions

Moving complicated expressions into boolean functions is another way to make long boolean expressions easier to read.

It works the same way as assigning items to variables.

For instance, we can write the following to put our expression into the boolean function:

const isInRange = (val) => {
  const MIN = 1;
  const MAX = 10;
  return val >= MIN && val <= MAX;
}

Since the function has a name, it’s easier to check what those expressions do from the name than from the boolean expressions.

Conclusion

Recursion shouldn’t be used for everything. If there’s an iterative solution to a problem, then we should use that.

If we’re booleans, then we shouldn’t compare values with true or false directly.

Finally, we can use a lookup table object instead of using conditional statements to check simple items.

Categories
JavaScript Best Practices

JavaScript Best Practices — Privacy

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 and use objects, including keeping things private.

Privacy Failures

When we return objects directly within a function that’s supposed to be private, we can modify them directly from the outside.

This is because the variables are passed by reference.

For instance, if we have:

function Foo() {
  const bar = {
    a: 1,
    b: 2
  };

  // public function
  this.getBar = function() {
    return bar;
  }
}

Then bar is private, but since it’s returned by getBar , we can access it from the outside.

We can also modify it.

For instance, if we write:

const foo = new Foo();
bar = foo.getBar();
console.log(foo.getBar());
bar.a = 2;
console.log(foo.getBar());

Then we’ll see that bar is now {a: 2, b: 2} .

This isn’t great since we don’t want to mutate bar from the outside.

Therefore, we should clone the object before returning it or put it in a module.

We can do a shallow clone with the spread operator.

And we can use the export keyword to export the object in a module.

This way, we either change the clone of the object or get an error if we try to modify it directly.

Object Literals and Privacy

If we want object data to be private, then we’ve to wrap it inside a function and then return parts of it.

For instance, we can write:

(() => {
  const name = 'foo';

  return {
    getName() {
      return name;
    }
  }
})()

getName has privileged access to the name constant in the example above.

We ran the function immediately after it’s defined, so we can call it if we assign the returned object to a variable or constant.

Also, we can assign the object to a variable that’s outside of the IIFE as follows:

let obj;
(() => {
  const name = 'foo';

  obj = {
    getName() {
      return name;
    }
  }
})()

Now we can call the obj.getName after the IIFE has been run.

Prototypes and Privacy

If we don’t want to have separate instances of methods in each instance of a constructor, then we’ve to put them in the prototype.

This way, all instance methods are inherited from the constructor’s prototype.

For instance, we can write:

function Foo() {}

Foo.prototype.hello = function() {
  //...
}

However, since constructors have no place to store private data, we’ve to think harder to find a place to store private data.

For instance, we can write:

function Foo() {}

Foo.prototype = (() => {
  const foo = "foo";
  return {
    getFoo() {
      return foo;
    }
  };
})();

In the code above, we have foo as the private variable.

And we return an object with the getFoo function.

Therefore, when we call getFoo as follows:

const foo = new Foo();
console.log(foo.getFoo());

We get 'foo' logged without exposing the foo constant itself.

This is one thing that the class syntax doesn’t have right now.

There’s no good way to add private variables with the class syntax, so this is a plus for the constructor syntax.

Revealing Private Functions As Public Methods

If we want to reveal some private functions as public methods, we can do that.

We can use the revelation pattern to reveal some private members of our choice.

For instance, we can write:

const obj = (() => {
  const foo = () => {
    //...
  }

  const bar = () => {
    //...
  }
  const baz = () => {
    //...
  }

  return {
    foo,
    bar
  }
})();

We have foo and bar returned in the object below, so we can call them with obj.foo() and obj.bar() .

This is handy for storing helper functions which we don’t want to expose to the outside.

We can freeze the obj object to prevent accidental modification.

Module Pattern

The module pattern is where we combined the private and privileged methods and namespaces into one.

For instance, we can define our own module by writing:

const APP = {
  utilities: {}
}

APP.utilities.math = (() => {
  return {
    add(a, b) {
      // ...
    },
    subtract(a, b) {
      // ...
    }
  };
}());

The code above defined a module named APP.utilities.math .

It has the add and subtract methods available to the outside.

We can call them whenever we want to by writing:

APP.utilities.math.add((1, 2)

or:

APP.utilities.math.subtract(1, 2)

We don’t have to return all the members in the returned object.

So we can leave some stuff private.

This is still useful for cases when we don’t have modules in our JavaScript project.

Conclusion

We can create our own modules for cases when we don’t have modules in our project.

To prevent accidental modifying returned objects in functions, we can make a copy of them or freeze them.

We can also have private members in constructor by assigning an IIFE to the prototype which returns an object.

Categories
JavaScript Best Practices

JavaScript Best Practices — Layout and Style

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 laying out and styling JavaScript code.

Layout Fundamentals

We want to have spacing and indentation so that we can actually read the code.

Therefore, we should have spacing like the following:

while (val < data[pos - 1]) {
  data[pos] = data[pos - 1];
  pos += 1;
}

data[pos] = val;

We have spaces after some keywords like while and operators.

Also, we have indentation before lines in blocks. 2 spaces are standard and are portable, so it’s a good amount of indentation to have.

After a block, we have a line to make things readable.

The Fundamental Theorem of Formatting

The Fundamental Theorem of Formatting states that good visual layout shows the logical structure of the program.

We got to format our code neatly so that we can see the logical flow of our program.

Human and Computer Interpretations of a Program

Humans and computers don’t read code the same way.

Humans need code to be neat and tidy so that we can read them easily.

The names have to be descriptive so that we can understand them.

Computers don’t need spacing and descriptive names to read programs.

Also, operations may mean different things for humans and computers.

For instance, if we have this expression:

3 + 1 * 2 + 8

Then humans may put the parentheses like:

(3 + 1) * (2 + 8)

A computer might read it as:

3 + (1 * 2) + 8

How Much Is Good Layout Worth?

A good layout makes ou code easier to understand.

However, small differences are fine. We just need something that’s easy to read.

To make our lives easier, we should just let code formatters tidy up the code for us and be done with it.

This will make our code easy to read for humans in a pinch.

Layout as Religion

Good programmers should be open-minded about code layouts.

There’re some layouts that are clearly bad, but arguing about small changes isn’t productive.

Objectives of Good Layout

A good layout should make our code easy to understand. They should achieve the following things.

Accurately Represent the Logical Structure of the Code

Logical structures should be accurately represented. Therefore, we should use indentation and spacing to show the logic structure.

Consistently Represent the Logical Structure of the Code

The layout should be consistent so that the code is easy to follow.

We should apply the same good styles everywhere.

This can be done easily with a good code formatter.

Improve Readability

Improving readability is important. If we understand the code easier, then we can work on it faster and make fewer bugs.

Withstand Modifications

Having a good layout makes our code hold up well to modifications.

We shouldn’t require to modify several other lines if we modify one line of code.

Also, we should minimize the number of statements that we need to change or add.

White Space

White spaces are important for readability.

In JavaScript, we usually indent with 2 spaces and have spacing in between operators and parentheses.

Anything separated by commas also has a space after the comma.

If we don’t have some spacing, it’s very hard to read the code.

Grouping

Grouping related statements are also important for distinguishing them from others.

Like we group thoughts into paragraphs in English, we should do the same with code.

We group related statements together and separate groups by one blank line.

Photo by Joshua Chun on Unsplash

Blank Lines

Blank lines are useful for separating groups of statements from each other.

Therefore, we should add them to make them easier to read.

Indentation

Indentations should be added to code inside a block so that we know that they’re inside the block.

2 spaces are pretty standard for indentation so we can consider using that when we indent.

Parentheses

Parentheses are also important to form groups of code. These are useful for grouping expressions.

For instance, we can use them to group arithmetic operations like:

let x = (3 + 1) * (2 + 8);

Also, parentheses are used for function signatures except for arrow functions that only have one parameter so we also use them there.

Conclusion

Code layout is important for improving readability.

We can clean up our code with a code formatter.

Indentation can be done with 2 spaces, parentheses are useful for grouping expressions.

Spaces and blank lines also make our code easier to read by grouping related code.