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.

Categories
JavaScript Best Practices

JavaScript Best Practices — Constructors and Classes

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 object creation and constructors.

Prototypes

When we have methods in our constructor, we should add them to the prototype property of the constructor instead of putting it inside the constructor function.

For instance, instead of writing:

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

We should write:

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

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

Even better, we use the class syntax by writing:

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

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

The last 2 pieces of code are the same.

We want to write our constructors like the last 2 examples because we only want to share one copy of the greet method between all the instances.

The greet method doesn’t change, only the value of this does.

Therefore, we don’t want to keep different copies of the same method as we would do in the first example.

Instead, we want to share it as we do in the last 2 examples.

We can create an instance of the constructor to find the difference:

const person = new Person('joe');
console.log(person)

If we log the person object, we can see that the method is in the __proto__ property, which is the prototype of the person .

It has the greet method.

If we create the same object with the version first version of the Person constructor, we’ll see that the greet method is in the person object itself.

Therefore, to save memory, the method should be in the constructor’s prototype property rather than inside the constructor itself.

Constructor’s Return Values

Constructors don’t have to return something explicitly.

We usually shouldn’t return anything explicitly because it’ll return the instance of the constructor automatically.

However, it does let us return something different if we want.

But we usually shouldn’t do that unless we have a reason to.

If we want to do that we can write:

function Person(name) {
  this.name = name;
  return {
    foo: name
  }
}

Then when we instantiate it as follows:

const person = new Person('joe');
console.log(person)

We get {foo: “joe”} instead of Person {name: “joe”} according to the console log output.

Patterns for Enforcing new

To create a new instance of an object, we need to use new .

The best way to do that is to use the class syntax.

This way we can’t call it without new .

If we call a constructor without new , then this would be the global object.

Naming Convention for Constructors/Classes

Constructors or classes are usually named in PascalCase.

This distinguishes regular functions from constructors and classes.

Using that

If we want to make sure that our constructor always behaves as a constructor, we can also create an object and return that.

For instance, we can write:

function Person(name) {
  const that = {};
  that.name = name;
  return that;
}

Now if we call our Person constructor with our without new , we still get an object returned.

Self-Invoking Constructor

We may also use the instanceof operator to check if a constructor’s this is the instance of the constructor.

Then we’ll know if we called it with new or not.

If we did, then the constructor’s this should be an instance of the constructor.

For instance, we can check as follows:

function Person(name) {
  this.name = name;
  if (!(this instanceof Person)) {
    return new Person(name);
  }
}

Now whether we called the Person constructor with new or not, we get the same result.

const person = new Person('joe');

and:

const person = Person('joe');

should get us the same result.

Conclusion

We should keep our methods in the prototype of the constructor rather than inside the constructor itself.

This way, one copy of the method is shared among all the instances.

This works because the method is the same across all instances.

The class syntax does this for us automatically.

Also, we can check if this is an instance of the constructor to check if our constructor has been called properly.

The class syntax also takes care of that for us since we can’t invoke a class without the new keyword.