Categories
JavaScript Best Practices

JavaScript Best Practices — Defensive Programming

JavaScript is a very forgiving language. It’s easy to write code that runs even if it has issues in it.

In this article, we’ll look at how to program defensively so that errors are handled gracefully.

Protecting Our Program from Invalid Inputs

We got to check our program for invalid inputs so that they won’t propagate in our systems.

For instance, we got to check for things like null , undefined , or invalid values all overt our code.

Also, we got to be aware of common security issues like SQL injection or cross-site scripting.

To do that, we do the following things in our program.

Check the Values of All Data from External Sources

We just can’t trust users to always enter valid data and that attackers won’t use exploits in our code.

Therefore, we should check everything thoroughly so that we can make sure that no invalid data gets propagated anywhere.

For instance, we should check for number ranges if code need numbers in a certain range.

Also, we shouldn’t use things like the eval function or the Function constructor which runs code from a string.

Check the Values of All Function Input Parameters

We got to check the values before we use them in a function.

For instance, we can write a function that dispenses beer to adults only as follows:

const dispenseBeer = (age) => {
  if (age < 21) {
    return 'you are too young';
  }
  //...
}

We check age before dispensing beer so that we won’t mistakenly dispense beer to underage people.

Decide How to Handle Bad Inputs

Bad inputs have to be handled in a graceful way.

We got to check them and then find a good way to deal with them.

Returning a message or throwing exceptions are good ways to handle invalid inputs.

For instance, our dispenseBeer function can be changed to show an alert box instead:

const dispenseBeer = (age) => {
  if (age < 21) {
    alert('you are too young');
    return;
  }
  //...
}

Or we can write the following:

const dispenseBeer = (age) => {
  if (age < 21) {
    throw new Error('you are too young');
  }
  //...
}

We don’t need the return since throwing exceptions would end the execution of the function.

Assertions

Also, we can use assertions to check for inputs before we run the function.

For instance, we can write the following using the assert module as follows:

const assert = require('assert');

const dispenseBeer = (age) => {
  try {
    assert.equal(age >= 21, true);
    //...
  }
  catch {
    return 'you are too young';
  }
}

This does the same thing as throwing an error since it checks if age is bigger than 21.

Otherwise, an assertion error would be thrown and ‘you are too young’ is returned.

This is the same as throwing an exception.

There’re many other kinds of assertions we can make with the assert module.

They include:

  • checking for equality
  • checking for inequality
  • checking for exceptions thrown
  • checking for deep equality
  • checking for deep inequality
  • …and others

Guidelines for Using Assertions

There’re some guidelines for using assertions.

Use Error-Handling Code for Conditions that are Expect to Occur

Error handling code should be used for checking for conditions that might be invalid.

In this case, we should handle those cases instead of using assertions.

Things like bad input values should be checked with error handling code.

Use Assertions for Conditions that Should Never Occur

If something should never occur, then we should use assertions to stop the code from running if they’re encountered.

We can run the rest of the code assuming that condition that we asserted to never occur won’t occur.

Avoid Putting Executable Code into Assertions

We should only use assertions to only check for things.

It keeps them clean and prevents any confusion.

Use Assertions to Document and Verify Preconditions and Postconditions

Preconditions that should be verified should have an assertion.

Since it;’s in the code, then we know what the codes check for when we read the code before running the rest of the code.

When the program ends, we can check for postconditions with assertions to check for conditions we look for.

Conclusion

We should check for invalid conditions before we run anything.

To do that, we can use assertions or error handling code. Assertions are better for checking for conditions that should never occur while error handling code are better for other cases.

Categories
JavaScript Best Practices

JavaScript Best Practices: Useless Code, Comparisons, and Eval

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 some JavaScript best practices to follow, including avoiding empty function and destructuring, null comparison, avoiding useless bind and avoid eval.


Don’t Write Empty Functions

Empty functions aren’t useful and we don’t know if they’re intentional or not. This means that we should at least write a comment to let people know whether it’s intentional.

This also goes for arrow function callbacks that return nothing, since they look similar to an empty object.

For instance, we should avoid:

function foo() {}
arr.map(() => {});
arr.map(() => ({}));

Instead, we should write:

function foo() {
  // do nothing
}

or:

arr.map(() => {}); // returns nothing

or:

arr.map(() => ({})); // returns empty object

Empty Destructuring Patterns

Empty destructuring patterns are also useless, so there’s no point in creating them. For instance, the following isn’t useful:

const {a: {}} = foo;

It might also be mistaken for setting a to an empty object as the default value as follows:

const {a = {}} = foo;

We should avoid having empty destructuring patterns in our code. If we have any, we can and should remove them.


Null Comparisons with ==

null comparisons with == or != also compares the operand we’re comparing with other values with undefined . Therefore, the following are also true if we compare foo with null in the following code:

let foo = undefined;
foo == null;

We get that foo == null returns true, which is probably not what we want. Likewise, if we have the following comparison with !=:

let foo = undefined;
foo != null;

We get that foo != null is false even though foo is undefined . Therefore, we should instead use === and !== to check for null as follows:

let foo = undefined;
foo === null;

and:

let foo = undefined;
foo !== null;

Never Use eval()

eval lets us pass in a string with JavaScript code and run it. This is dangerous because it can potentially let anyone run JavaScript code that we didn’t write. It can open up our program to several kinds of injection attacks.


No Extension of Native Objects

In JavaScript, we can extend any native objects with their own methods. However, that’s not a good idea because it may break other pieces of code that use native objects in ways that we don’t expect.

For instance, if we have the following:

Object.prototype.foo = 55;

const obj = {
  a: 1,
  b: 2
};

for (const id in obj) {
  console.log(id);
}

We see that foo is logged in addition to a and b because the for...in loop iterates through all enumerable properties in the current object and the object’s prototype chain.

Since JavaScript extends the JavaScriptObject object by default, the for...in loop will also loop through enumerable properties in the Object’s prototype.

Therefore, we shouldn’t extend native objects as it may do things that we don’t expect to other code.


No Unnecessary Function Binding

The bind method is a method of a function that changes the value of this inside a traditional function. Therefore, if our function doesn’t reference this, then we don’t need to call bind on it to change the value of this.

For example, the following function uses bind with a purpose:

function getName() {
  return this.name;
}
const name = getName.bind({
  name: "foo"
});
console.log(name());

In the code above, we have the getName function, then we called bind on it to change the value of this to { name: “foo” }. Then we when we call name in the last line, we see return this.name , which should be 'foo' since we changed the value of this to the object.

On the other hand, if we have:

function getName() {
  return 'foo';
}
const name = getName.bind({
  name: "foo"
});
console.log(name());

Then the call to bind is useless since getName didn’t reference this. Therefore, if our function doesn’t reference this, then we don’t need to call bind on it.


Conclusion

Comparing null with == isn’t very useful since expressions like undefined == null also return true because of type coercion. To ensure attackers can’t run malicious code, eval shouldn’t be called.

bind is useless if we don’t reference this in our function, so if we do call it, we should make sure that we have this in our function. Other things like empty functions and destructuring patterns are useless, so they should be avoided.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes and 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 we create classes and functions.

Avoid Creating God Classes

God classes are classes that are all-knowing. If a class is mostly used for retrieving data from other classes using get and set methods, then we don’t need the class.

So if we have something like this:

class Foo {
  getBar() {}
  setBar() {}
  getBaz() {}
  setBaz() {}
  getQux() {}
  setQux() {}
}

We can remove the class, and access the things we need directly.

Eliminate Irrelevant Classes

If we need a class, then we should remove it.

This applies to classes that only have data. If we have members that are in the class, then we should consider if they should be members of another class instead.

Avoid Classes Named After Verbs

We shouldn’t name classes with verbs since a class that has only behavior but not data shouldn’t be a class.

For instance, if we have:

class GetFoo {
  //...
}

Then it should be a function instead of a class.

When Should We Create Functions?

We should create functions to make our code better. The following are the reasons for creating functions.

Reduce Complexity

Reducing complexity is one of the most important reasons for creating functions.

We got to create them so that we don’t repeat code and minimizing code size.

If we write all procedures without functions, then we would have lots of similar code.

For instance, if we have:

const greet1 = `hi joe`;
const greet2 = `hi jane`;

Then we can make a function to generalize that:

const greet = (name) => `hi ${name}`;
const greet1 = greet('joe');
const greet2 = greet('jane');

Now we can call greet anywhere to create similar strings.

Introduce an Intermediate, Understandable Abstraction

The code above is also an abstraction. We generalized the code for returning greeting strings.

We can pass in different values of name and return a new string.

Avoid Duplicate Code

Avoiding duplicate code is also a good reason for creating functions.

Again, as we can see from the example above, we can call greet to create many more of the same strings.

Then we don’t have to repeat the hi part everywhere.

Support Subclassing

We can create a method in a subclass to override a method in the superclass.

For instance, we can write the following code to do that in JavaScript:

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} speaks`;
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }
  speak() {
    return `${super.speak(this.name)} in dog language`;
  }
}

We have overridden the speak method in Dog by calling super.speak .

Therefore, we can keep the speak method in Dog simpler.

Photo by Danielle Cerullo on Unsplash

Hide Sequences

Functions are good for hiding implementations of procedures.

For instance, we can have a function that calls other functions and do something with them.

We can write:

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

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

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

const getAllDimensions = () => {
  const weight = calcWeight();
  //...
  const height = calcHeight();
  //...
  const width = calcWidth();
  //...
}

We called multiple functions in the code above. But other parts of our program won’t know the order that they’re called.

This reduces the implementation that is exposed and reduces coupling.

Improve Portability

Functions can be moved anywhere easily. We can move it and anything else that depends on it easily.

Simplify Complicated Boolean Tests

If we have long boolean expressions, then we should put them into their own function.

For instance, if we have:

const isWindowsIE = navigator.userAgent.toLowerCase().includes('windows') &&
  navigator.userAgent.toLowerCase().includes('ie');

Then we can put the whole thing into a function by writing:

const isWindowsIE = () => {
  const userAgent = navigator.userAgent.toLowerCase()
  return userAgent.includes('windows') &&
    userAgent.includes('ie');
}

It’s just much cleaner than having long boolean expressions.

Conclusion

God classes, classes that only data or behavior are all bad. We should make sure that we have a mix before creating both.

We create functions to reduce complexity, remove duplicate code, and make abstractions.

They’re good for creating anything.

Categories
JavaScript Best Practices

JavaScript Best Practices — Improving Classes

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

In this article, we’ll look at the best practices for creating classes and when we should create them.

Constructors

There are a few things that we should do to make our constructors better. They are the following.

Initialize All Member Data in All Constructors If Possible

We should put them all in the constructor so that they’re all initialized when we instantiate the object.

So we can write:

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

Now we make sure that everything’s initialized with a value.

Create a Singleton In the Constructor

If we need only one instance of a constructor, then we can create one instance of it.

For instance, we can write the following:

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

In the code above, we return the object that we created if this.instance isn’t defined yet.

Otherwise, we return whatever it’s set to this.instance .

Prefer Deep Copies to Shallow Copies Until Proven Otherwise

Deep copies copy everything, so that’s a lot better than doing a shallow copy. Shallow copies leave some things referencing the original object.

That’s not good if we want a true copy.

Therefore, we’ve to make our code to make deep copies as follows:

const copy = obj => {
  const copied = {
    ...obj
  };
  for (const k of Object.keys(obj)) {
    if (typeof obj[k] === 'object') {
      copied[k] = {
        ...copied[k]
      };
      copy(copied[k]);
    }
  }
  return copied;
}

We just use the spread operator to copy nested objects if one is found. And do the same thing recursively.

Then we return the object that we copied.

When Should We Create a Class?

We shouldn’t always create classes. There a few scenarios where it makes sense to create a class.

Model Real-World Objects

Classes are great for modeling real-world objects since they model the behavior of objects

They let us encapsulate instance variables and methods into one package to store state and do actions on objects respectively.

Model Abstract Objects

Likewise, we can use classes to model abstract objects.

They can be used to make abstractions, which are generalizations of different kinds of objects.

Classes are great for holding shared members of subclasses. And subclasses can inherit from them.

However, we should keep the inheritance tree simple so that people won’t be confused with the code.

Reduce Complexity

We can use classes to reduce the complexity of a program.

Classes are great for hiding information. In JavaScript, there’re no private variables in classes yet, so we’ve to hide data in methods instead.

We can then minimize coupling between different parts of our program with that.

Hide Implementation Details

Methods are also good for hiding implementation details.

We can hide the details within methods and only run things that are needed.

To do that, we can nest functions and variables inside methods.

Limit Effects of Changes

The effects of changes can be reduced since we can hide things.

As with hiding implementation, the effects of changes can be isolated by limiting the effects of changes within methods.

Hide Global Data

Global data can become private data by putting them inside the methods of a class.

Then they don’t have to be exposed to the public. All we have to do is to use let and const to declare them within methods.

Streamline Parameter Passing

If we have the same parameters passed into different functions, then we can change the parameters to instance variables and the functions to methods.

For instance, if we have:

const speak = (name) => `${name} spoke`;
const greet = (name) => `hi, ${name}`;

Then we can put the methods into their own class as follows:

class Person {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} spoke`;
  }
  greet() {
    return `hi, ${this.name}`;
  }
}

Now we don’t have to pass in name everwhere.

We just make an instance of Person and call those methods without passing in any arguments.

Conclusion

We can create classes to encapsulate data and package things together. However, we shouldn’t create classes for everything.

Also, we should make deep copies rather than shallow copies whenever possible.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes

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

In this article, we’ll look at the best practices for creating classes.


ES6 Class Basics

To make the prototypical inheritance model of JavaScript less confusing, the class syntax has been introduced in ES6.

It has the constructor , instance variables, and methods just like classes in languages like Java do, but they act differently.

For instance, we can create a class by writing:

We have the name instance variable and the speak method. this is the instance of the class itself.


Getters and Setters

Getters are methods that only return values, whereas setters are methods that let us set a value to an instance variable.

They let us do either in a controlled manner rather than accessing instance variables directly.

We can also add getters and setters to the class as follows:

With these getters and setters, we can write the following to get name:

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

And set name:

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

Child or Subclasses

We can also create child classes with the extends keyword. For instance, we can write:

We created a Dog class that overrides the speak method of the Animal class.

Also, we call the parent constructor with super in the Dog’s constructor and call super.speak to call the speak method of Animal.

This is much cleaner than the old constructor function syntax for inheritance.


Static Methods and Properties

We can add static properties to a class by using the static keyword.

Doing that will help us add a property to it that’s shared by all instances.

For instance, we can write:

class Dog {
  static type() {
    return 'dog';
  }
}

Then we can call it as a static method as follows:

console.log(Dog.type())

To create static properties, we write the following:

class Dog {}
Dog.type = 'dog';

console.log(Dog.type);

We set the type property to 'dog' and it’s shared across all instances.

This syntax is valid since classes are just functions and functions are treated the same as any other object in JavaScript.


Private Fields

Future versions of JavaScript can also let us define private class fields. There’s no equivalent of this in constructor functions.

They are denoted by the # symbol in front of the field name.


Conclusion

We can define constructors with the class syntax to make them look easier to understand than constructor functions.

The classes look similar to Java classes but act very differently underneath. We have to understand the underlying behavior before we use it. This means that we should understand prototypical inheritance before using JavaScript classes.