Categories
JavaScript Best Practices

Maintainable JavaScript — Handling Errors

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at ways to handle errors.

Advantages of Throwing Errors

Throwing our own errors allow us to provide the exact text to be displayed by the browser.

We can include any information we like in addition to the line and column numbers.

The more information is given, the easier it’ll be for us to find the issue.

For instance, if we have:

function getDivs(element) {
  if (element && element.getElementsByTagName) {
    return element.getElementsByTagName("div");
  } else {
    throw new Error("divs not found");
  }
}

Then we throw an error is there’re no divs found in the element.

Knowing this makes the problem easier to find.

When to Throw Errors

Throwing errors everywhere is impractical and decrease our app’s performance.

For instance, if we have:

function addClass(element, className) {
  if (!element || typeof element.className !== "string") {
    throw new Error("element class name must be a string");
  }
  if (typeof className !== "string") {
    throw new Error("class name must be a string");
  }
  element.className += ` ${className}`;
}

then most of the function is taken up by error throwing code.

This is overkill in JavaScript.

We should only throw errors for code that’s the most likely to throw errors.

So we don’t need to throw error for the className parameter.

Instead, we write:

function addClass(element, className) {
  if (!element || typeof element.className !== "string") {
    throw new Error("element class name must be a string");
  }
  element.className += ` ${className}`;
}

element.className is most likely not to be a string, so we check for that.

Error checking isn’t necessary for known entities.

If we can’t identify the places where the function will be called ahead of time, then it’s a good idea to throw errors if something is unexpected.

JavaScript libraries should throw errors from their public interfaces for known error conditions.

This way, programmers can know where the issue is if they used it incorrectly.

The call stack should terminate in the library’s interface.

This also applies to private libraries.

Once we fixed hard to debug errors, we should throw some errors that let us identify the problem easier.

If we’re writing code that may mess up something if it does something unexpected, then we should check for those conditions and throw errors.

If the code is going to be used by someone we don’t know, then the code’s public interface should throw errors for things that are unexpected.

The try-catch Statement

JavaScript provides us with the try-catch statement to let us intercept thrown errors before they’re handled by the browser.

The code that can cause an error is in the try block and the error handling code is in the catch block.

For instance, we can write:

try {
  errorFunc();
} catch (ex) {
  handleError(ex);
}

We can also add a finally clause below the catch block.

The finally block has code that’s run regardless of whether an error occurs.

For instance, we can write:

try {
  errorFunc();
} catch (ex) {
  handleError(ex);
} finally {
  cleanUp();
}

The finally clause can be tricky.

If the try clause has a return statement, then it won’t return until finally has run.

Conclusion

We should only throw errors at the interface level and for errors that occur most often.

Also, we can use the try-catch-finally clauses to handle errors.

Categories
JavaScript Best Practices

Maintainable JavaScript — Facade, and Object Freezing

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at some design patterns.

Facade Pattern

The facade pattern is a design pattern that lets us create a new interface for existing objects.

Facades are also called wrappers because they wrap existing objects with a different interface.

For instance, we can create a class with methods to manipulate DOM objects.

We can write:

class DOMWrapper {
  constructor(element) {
    this.element = element;
  }

  addClass(className) {
    element.className += ` ${className}`;
  }
}

We have a constructor that takes an element object which is a DOM element.

Then we have an addClass method to let us add the className to the className string property.

Then we can use it by writing:

const wrapper = new DOMWrapper(document.querySelector("div"));
wrapper.addClass("selected");

A wrapper class is useful for extending the functionality of existing objects.

We have complete control over the interface so that we can control the functionality that’s available to the user.

Also, we can add new methods that are simpler to use than existing ones.

Polyfills

Polyfills becomes popular when ES5 + and HTML5 features are being added to browsers.

We can use polyfills to make features available to our apps before they’re added to browsers.

Many methods of built-in objects like forEach , map , filter , etc. of arrays are added after the first version of JavaScript.

This may not be available in older browsers, so we have to add polyfills for ones that don’t.

Once the browsers our app supports implemented the features, then we no longer need the polyfill.

We can easily remove them when we only support browsers with native functionality.

When we pick a polyfill, we should make sure it matches the native version as much as possible.

Preventing Modification

ES5 added some methods to prevent the modification of objects.

They’re supported in almost all modern browsers.

We can prevent the adding properties to an object by using the Object.preventExtensions method.

The Object.seal method has the capabilities of preventExtensions . And it prevents existing properties and methods from being deleted.

The Object.freeze method has the capabilities of seal , and it prevents existing properties and methods from being modified.

So we can write:

const person = {
  name: "james"
};
Object.preventExtensions(person);

Then we can call the Object.isExtensible method to check if the object is frozen or not:

console.log(Object.isExtensible(person));

If it’s false , then it’s frozen.

And if we try to add a property to it:

person.age = 50;

it’ll fail silently unless the code has strict mode enabled.

Likely, we can call Objec.seal() to seal an object.

Then we can write:

const person = {
  name: "james"
};

Object.seal(person);
console.log(Object.isSealed(person));

We sealed the object, so isSealed should return true .

Now if we try to add or remove properties:

person.age = 50;
delete person.name;

they’ll both fail silently unless strict mode is on.

Likewise, we can use the Object.freeze method to freeze an object.

We can write:

const person = {
  name: "james"
};

Object.freeze(person);

Then we can check if we can modify our object by writing:

console.log(Object.isExtensible(person));
console.log(Object.isSealed(person));
console.log(Object.isFrozen(person));

person.name = "mary";
person.age = 25;
delete person.name;

isExtensible should return false and the other 2 should be true .

The object modifying code should fail silently if strict mode is off.

And if strict mode is on, then we should see error messages.

Conclusion

There’re various Object static methods to stop us from changing our objects.

Also, we can create a facade around our existing code to create a new interface for them.

Categories
JavaScript Best Practices

Maintainable JavaScript — Error Class and Checks

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at the best ways to handle errors.

try-catch

We should never have try-catch statements with an empty block.

They should be handled in some way so that our code won’t fail silently.

For instance, we don’t write:

try {  
  errorFunc();  
} catch (ex) {  
  // do nothing  
}

We don’t want to ignore errors.

Error Types

The ES specification specifies several kinds of built-in errors type.

Error is the base constructor for all errors.

EvalError throws when an error occurs during execution of eval .

RangeError is throw when a number is outside the bounds of its range.

They rarely occur during normal execution.

ReferenceError is thrown when an object is expected but it’s not available.

SyntaxError is thrown when code has a syntax error.

TypeError is thrown when a variable is of an unexpected type.

URIError is thrown when the URI we passed into encodeURI , encodeURIComponent , decodeURI , or decodeURIComponrnt is incorrectly formatted.

If we understand the different types of errors, then it’s easy to handle them.

All error types inherit from error.

And if we check for specific types of errors, we get better error handling.

For instance, we can write:

try {  
  // ...  
} catch (ex) {  
  if (ex instanceof TypeError) {  
    // ...  
  } else if (ex instanceof ReferenceError) {  
    // ...  
  } else {  
    // handle all others  
  }  
}

We check which type of error is thrown with the instanceof operator, and we can write error-handling code for each kind of error.

Throwing Error instances have their advantages.

Error objects thrown are handled by browser’s error handling mechanism.

There’s also extra information in the Error object like the line and column number and stack trace.

If we want to throw custom errors, we can create our own Error class.

For instance, we can write:

class MyError extends Error {  
  constructor(message) {  
    super();  
    this.message = message;  
  }  
}

We used the extends keyword to create a subclass of Error .

Then we can use it by writing:

throw new MyError('error occurred');

Now if we throw the error, then it’ll respond like any native error.

Most modern browsers will handle them the same way so we don’t have to worry about browser compatibility.

With our own error class, we can check for the error type by writing:

try {  
  // ...  
} catch (ex) {  
  if (ex instanceof MyError) {  
    // ...  
  } else {  
    // handle all others  
  }  
}

We check the error type with instanceof like any other error.

Conclusion

We can create our own error class so that we can include the information we want in the error.

Also, we check for the type of error that’s thrown with the instanceof operator.

JavaScript has several types of built-in error types.

Categories
JavaScript Best Practices

Maintainable JavaScript — Wrappers and Coupling

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at wrapper types and loose coupling.

Primitive Wrapper Types

Primitive wrapper types are instances of the String , Boolean , and Number constructors.

They’re little known and often misunderstood pars of the JavaScript language.

They all exist as constructors in the global scope and each represents the object form of the respective primitive type.

They make primitive values act like objects.

For instance, we can write:

const name = 'james'.toUpperCase()

to return 'james' in upper case form.

What it’s done in this line is that 'james' is converted into an object with the String constructor.

Then we called the toUpperCase method in the string instance.

This is what JavaScript engines do under the surface.

After the result is returned, it’s destroyed automatically.

Another one will be created when it’s needed.

It’s possible to use these constructors in our own code.

For instance, we can write:

let name = new String("james");  
const author = new Boolean(true);  
const count = new Number(10);

We used the primitive wrapper constructors to create wrapper objects.

However, they make the code longer and more confusing, so we should avoid them.

The Google and Airbnb style guides all flag them.

And linters like ESLint and JSHint will all warn us if they exist.

Loose Coupling of UI Layers

JavaScript is often used for creating web app UIs.

We create them with the HTML is used to define the semantics of the page.

CSS is used for styling and layout of the page.

And JavaScript adds dynamic behavior to our page to make it more interactive.

CSS and JavaScript act like siblings.

It doesn’t have a dependency on CSS, but we can use it to do the styling

We can have a page with just HTML and CSS.

And it’s possible to have a page with just HTML and JavaScript.

To make web UI code maintainable, need to make the coupling of each part loose.

Tight coupling means that one component has direct knowledge of the other and if one changes, then the other would also change.

For instance, if the alert CSS class is used everywhere in our HTML, then when we need to change the alert class to something else, then we need to change it somewhere else.

Loose coupling is achieved when we can make changes to a single component without affecting the others.

We want to make code changes easy, so we need components to be loosely coupled.

However, there’s no way that 2 components have no coupling.

They’ll have to interact with each other somehow.

A loosely coupled web UI is easier to debug.

We can look at HTML to check the text or structure.

When styling issues arise, we know it’s probably in the CSS.

And if there’re any behavior issues, then we can look at the JavaScript.

Conclusion

We should keep our code loosely coupled and avoid using primitive wrapper constructors in our code.

Categories
JavaScript Best Practices

Maintainable JavaScript — Throwing Errors

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at ways to throw errors.

Errors

We throw errors when our program encounters some unexpected.

Maybe an incorrect value was passed into a function or operation.

Programming language defines rules and when we deviate from them, then errors should be thrown.

Debugging would be close to impossible if errors aren’t thrown and reported back to us.

If everything failed silently, then it’ll take a long time to find out what’s wrong with our program.

Errors are there to help us.

The problem we have is that errors may come up in unexpected places and times.

Default error messages may not give us enough information to solve the problem.

It’s always a good idea to plan for failure so that we can deal with them before they occur.

Throwing Errors in JavaScript

To throw errors in JavaScript, we use the throw keyword with an Error instance.

For instance, we can write:

throw new Error("an error occurred.")

We pass in a message that we want to display with the error.

The built-in Error type is available in all JavaScript implementation.

The constructor takes a single argument, which is the error message.

When an error is thrown and it isn’t caught by a try-catch block, then the browser displayed the message in the browser’s way.

Most browsers have a dev console to display errors in there.

An error we throw is treated the same as an error that we didn’t throw.

A bad way to use the throw keyword is to use it with something that isn’t an Error instance.

For instance, we may write something like:

throw "error";

This is bad because browsers may not respond the same way to this.

And also, we throw away useful information like the stack trace.

However, we can throw anything we like.

There’s nothing prohibiting this.

Firefox, Opera, and Chrome convert non-error objects to string when they’re thrown.

And IE doesn’t.

Advantages of Throwing Errors

Throwing our own error allows us to provide the exact text to be displayed by the browser.

Instead of line and column numbers, we can include any information we like that we’ll need to successfully debug the error.

For instance, if we have:

function getSpans(element) {  
  if (element && element.getElementsByTagName) {  
    return element.getElementsByTagName("span");  
  } else {  
    throw new Error("argument not a DOM element");  
  }  
}

Then we throw an error if an object isn’t a DOM element.

This way, we know that we didn’t pass in DOM elements if we see the error message.

With this setup, we see the error message that can help us debug what’s wrong with our code without looking too hard.

Conclusion

Throwing errors lets us know what’s wrong when we run into problems.

Without them, we won’t know what’s wrong when our code does something we don’t expect.