Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Avoiding Bad Code

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 some bad code that should be avoided if we want to write more robust JavaScript code.

Avoid Large Modules

Large modules are bad because they have too many members, which makes them hard to track and debug.

Longer pieces of code are also hard to read.

Modules should be small. For instance, we can have one module that only does mathematical operations.

We can write something like the following:

export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;

The code above is short and easy to read.

Avoid Modules With Multiple Responsibilities

Modules with multiple responsibilities are probably doing too much.

This violates the single responsibility principles which are bad because it’s confusing to have many different things going on in one piece of code.

Modules with multiple responsibilities are confusing for many people since they do too many things. It’s not logical to have one module to be doing more than one thing.

For instance, if we have:

export const add = (a, b) => a + b;
export const foo = () => console.log("foo");

then that’s bad because we have functions in a module that do multiple kinds of things.

We have an add function that adds 2 numbers, and another function that logs 'foo' .

This doesn’t make sense since it does multiple things. This makes using it harder since we’ve to look at it to find out what the module does.

If the module only does one thing and the module name suggests that it’s so, then people don’t have to look too hard to find out what a module is doing.

Avoid Tight Coupling

Tight coupling is bad because it’s easy to break code when we need to change it, which is inevitable.

To avoid tight coupling, we should avoid exposing too many members from a module to the outside.

When 2 modules are tightly coupled, this means that 2 classes have to change together.

On the other hand, loose coupling of modules means that they’re mostly independent.

Other entities that may be tightly coupled include classes and functions. For instance, if we have the following code:

index.js

import { greet } from "./module";
export class Person {
  constructor(name) {
    this.name = name;
  }

  greet(greeting) {
    greet(`${greeting} ${this.name}`);
  }
}

module.js

import { Person } from "./index";
export const greet = greeting =>
  console.log(`${greeting} ${new Person("foo")}`);

In the code above, we have the greet function outside index.js and we imported Person in the greet function. In index.js , we have the Person class that imported the greet function from module.js .

This kind of dependency structure is too tightly coupled because Person depends on greet and greet depends on Person .

When one changes, we’ve to worry if it breaks the other, which isn’t good since it makes our code more fragile and slows us down when we try to make changes.

It’s better for them to be independent. If we don’t need them to be dependent on the other, then we should eliminate that dependency.

For instance, we can write the following:

index.js

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

  greet(greeting) {
    console.log(`${greeting} ${this.name}`);
  }
}

module.js

export const greet = greeting => console.log(greeting);

In the code above, we kept them independent by eliminating the imports and referencing of the dependencies.

This is much cleaner and we don’t have to worry about breaking things outside of the code the module the items are in since they don’t depend on each other.

Avoid Magic Numbers

Magic numbers are numbers that occur in multiple places with an unexplained meaning. They’re often used as constants.

Since they’re used as constants, they should be replaced with named constants.

For instance instead of writing the following:

const foo = 1;
const bar = 1;
const baz = 1;

We should use a named constant instead of 1. For instance, we can write the following:

const CONSTANT = 1;
const foo = CONSTANT;
const bar = CONSTANT;
const baz = CONSTANT;

This way, we know that 1 is constant and give it meaning. Constant names in JavaScript are usually upper-case to make it clear that it’s a constant.

Conclusion

To write robust JavaScript, we should create small modules that only does one thing.

Tight coupling between entities should be eliminated. Finally, magic numbers should be avoided and should be replaced with constants.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Error Prevention

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 ways to write more robust code when dealing with JSON and best practices when writing code to handle errors better and to prevent them from happening.

Use try…catch When Using JSON.parse or JSON.stringify

JSON.parse and JSON.stringify will throw errors if they encounter issues.

Whenever JSON.parse encounters invalid JSON in a string, it’ll throw an error. JSON.stringify throws errors when it tries to convert a circular structure to JSON.

Therefore, we should use try...catch to wrap around JSON.stringify and JSON.parse to prevent errors from stopping our program from running.

For instance, we should write something like:

try {  
  const json = '{}';  
  const parsed = JSON.parse(json);  
} catch (ex) {  
  console.error(ex);  
}

so that JSON.parse won’t crash our program when it runs into issues parsing JSON. We can also replace JSON.parse with JSON.stringify for code that uses that.

Use ESLint or Another Linter to Catching Errors Early

ESLint is a standard linter for JavaScript. It checks for syntax errors and confusing code to prevent developers from committing many kinds of problematic code or potential bugs.

It has a big list of rules which check for possible errors or useless and confusing code.

For instance, it checks for return statements in getters. This because there’s no point in having a getter that doesn’t return anything in a JavaScript or class. A getter that returns nothing is useless and it’s probably a mistake if it’s there.

Other things include disallowing the assignment operator in conditional expressions, as we probably want to use == or === to compare things instead and we forgot the extra equal sign.

There’re many more rules located at https://eslint.org/docs/rules/.

It’s easy to set up for any project. We can just install eslint in our project folder by running:

npm install eslint --save-dev

Then we create ESLint configuration file by running:

npx eslint --init

to customize the rules that we want to enable. It’s very flexible and we don’t have to enable checks for all the rules.

It also has lots of plugins to add more rules. Popular rules include Airbnb and the default rules. We can also include Node.js rules, Angular, React, Vue, etc.

Don’t Mess With Built-in Object Prototypes

We should never mess with prototypes of built-in global objects. Objects like Object , String , Date , etc. all have instances methods and variables in their prototypes which shouldn’t be messed with.

If we change them by mistake, then our code probably won’t work the way we expect them to since they often call instance methods from these built-in objects and manipulating them in our code will probably break our code.

The member of native objects may also have name collisions with members in our own code.

It’s also confusing since if we change the built-in object’s prototypes, a lot of people may think that it’s actually a method that’s part of the standard library.

We don’t want people to make that mistake and write more bad code and create more issues down the road.

Therefore, we should never modify built-in objects and their prototypes.

If we need any new functionality that built-in native objects don’t provide, then we should write our own code from scratch and reference built-in native items if necessary.

Also, if we want to use new native methods that don’t exist in the browser your app is targeting, then we can use polyfills to add that functionality instead of implementing it from scratch ourselves.

Photo by Sean McGee on Unsplash

Always Use Strict Mode

JavaScript used to let people do a lot of things that people aren’t supposed to do.

Strict mode throws errors when we write code that isn’t supposed to be written, like assign null or undefined with a value, or accidentally creating global variables by omitting var , let or const .

Other things that throw errors include trying to extend objects new properties that have the Object.freeze method applied to it.

For instance, the following will do nothing without strict mode:

const foo = {};  
Object.preventExtensions(foo);  
foo.newProp = 'foo';

However, with strict mode on:

'use strict'  
const foo = {};  
Object.preventExtensions(foo);  
foo.newProp = 'foo';

We’ll get an error ‘Uncaught TypeError: Cannot add property newProp, object is not extensible’.

It also prohibits us from using keywords and constructs that may be used in future versions of JavaScript. For instance, we can’t declare a variable called class since it’s a reserved keyword.

Strict mode is applied to all JavaScript modules by default. However, old-style scripts don’t have strict mode on by default. Therefore, we should make sure that those have strict mode on by adding 'use strict' to the top of our code.

We can also add 'use strict' inside a function, but we definitely want strict mode everywhere so we should just put it on top of the script file.

Since strict mode prevents us from committing so many kinds of errors, we should always have it on.

Conclusion

Preventing JavaScript code errors is easy with ESLint and strict mode. They prevent us from making so many kinds of errors that it’ll save us a lot time from debugging bad code.

Also, we should use try...catch to catch errors when parsing and stringifying JSON.

Categories
JavaScript Best Practices

JavaScript Best Practices for Creating Objects

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

In this article, we’ll look at some best practices for writing robust JavaScript code.


Use Factory Functions

Factory functions are functions that return a new instance of a constructor or class.

We can use them to create objects without writing any code with the new keyword to instantiate classes or constructors.

Constructors are often a confusing part of JavaScript and definitely one we can avoid if we wish to.

For instance, we can make our own factory function as follows:

In the code above, we have the createPerson factory function that takes a firstName and lastName parameter and returns an object with the firstName, lastName, and the fullName method.

We used an arrow function so that we don’t get confused with the value of this that’s in the returned object. Since arrow functions don’t bind to this, we know that the value of this in fullName is the returned object.

Factory functions can return objects with anything, so the possibilities are endless.

To use our createPerson factory function, we can write the following code:

const person = createPerson('Jane', 'Smith');
const fullName = person.fullName();

We created a person object with the createPerson function and then called the fullName method on it.

Then we get that fullName is ‘Jane Smith’ since this references the person object.

Many JavaScript functions like Number and Boolean are factory functions. They take any object as an argument and then return a number or boolean, respectively.

It makes our JavaScript code more robust because we don’t have to worry about class or constructor instances. We just have to think in terms of objects and composing functions.

Composing functions makes code more reusable and testable, creating more robust code because of this.


Create Instance Methods for Constructors by Attaching to the prototype Property

When we want to create instance methods of a constructor, we should attach the methods to the prototype property of the constructor.

This way, when we instantiate the constructor, we can also call the methods on the instance.

A method that’s attached directly to the function is a static method that’s shared by all methods.

For instance, we can add instance methods to a constructor by attaching it to its prototype property as follows:

In the code above, we created a Fruit constructor that returns a Fruit instance. Then we added the grow instance method by writing:

Fruit.prototype.grow = function() {
  console.log(`${this.name} grew.`);
}

Then when we instantiate it as follows:

const fruit = new Fruit('apple');
fruit.grow();

Instance methods encapsulate methods inside the constructor instance, preventing it from being exposed to outside code and allowing it to make accidental changes.

Therefore, this makes our code more robust. Also, every constructor has its own responsibility since it has its own instance method. This prevents multiple constructors from doing different things to the same object.

The alternative (above) creates a copy of the grow instance method for each instance. The method isn’t cached, so it’s slower to use attach instance methods this way.


Use the .type Property to Create Factory Functions That Can Create Multiple Types of Objects

Since factory functions can be composed, we can create a factory function that can create multiple types of objects.

We can differentiate between them with the type property, which is a conventional way to distinguish between different types of objects that we want to create.

To do this, we can write the following code:

In the code above, we have the createPet factory function that calls the createDog or createCat factory function depending on what kind of pet type we pass into createPet.

We also composed factory functions by calling a factory function inside the createPet factory function.

Now we just have to worry about one factory function (createPet) instead of using multiple factory functions to create different kinds of objects. This hides the complexity that’s underneath the code and thus there’s less risk of breaking things when we make changes.


Conclusion

With factory functions, we can create them to create new objects. We can also compose them and call different ones within one factory function to do different things.

When we have a constructor function, we should attach instance methods to the prototype property of the constructor function so that they don’t have to be recreated all the time.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code

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

In this article, we’ll look at how to check for device types and status to make sure that our apps degrade gracefully.


Device Type Check

We should check the type of the device to see what features should be enabled. This is important since web apps don’t only work on desktops and laptops anymore.

Now we have to worry about mobile devices with various operating systems.

We can check the device type from the browser’s user agent. The global navigator object has the userAgent property that has the user agent, which has the operating system information as part of the user agent string.

For instance, we can use it as follows:

const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)

In the code above, we check for all the possible substrings in the user agent string for mobile operating systems. We mostly care about Android, iPad, and iPhone, but testing the other ones isn’t that hard.

It’s easy to spoof the user agent string to make our browser pretend to be something else. However, most people don’t do it and there aren’t many reasons for other people to do it, so it’s still a good way to check if a device is a mobile device and act accordingly.


Checking for Network Connection Status

Like checking for user agents, checking for the network connection status is important since lots of devices can go offline at any time.

Mobile devices move between fast and slow networks or to a place without cell phone signal reception. As such, we have to check for that so our apps fail gracefully when a device is offline.

Most browsers now support the Network Information API, so we can use it to check for network status easily.

We can check for the network connection type as follows:

In the code above, we just retrieve the connection type with the connection, mozConnection, or webkitConnection properties to get the initial connection object.

Then we use the effectType property from the connection object to get the effective network type.

Next, we add a change event listener to the connection object to watch for connection type changes.

As we can see, we don’t have to do much to watch for internet connection type changes. Therefore, we should definitely do this.

If we change the network connection type from the Network tab in Chrome to simulate network connection type changes or change network connection types in real life, we should then see console log outputs like this:

4g to 2g  
2g to 4g  
4g to 4g

Checking for Feature Availability

Different features may be available for different browsers by default. We should check if the features we want are supported in the browser before we implement any feature.

We can check if a feature is supported by a browser with Can I Use before using a library that might not be supported.

There’s also the Modernizr library to detect features that are available within the given browser.

We can install Modernizr by adding a script tag:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>

It comes with classes that we can use to toggle HTML elements on and off depending on whether something is available.

For instance, it comes with the cssgradients and no-cssgradients classes to let us add different styles for situations where CSS gradients are available and not available, respectively.

With Modernizr installed, we can do the following in our CSS code:

In the code above, we have a fallback image for situations where the CSS gradients feature isn’t available, as was the case inside the .no-cssgradients header selector.

And we have our normal CSS gradients in the .cssgradients header selector.

Then we can add our own tests for features like checking if jQuery is available after adding the script tag above:

window.Modernizr.addTest("hasJquery", "jQuery" in window);

Conclusion

We should check for the device type by using the user agent. Even though it’s easily spoofable, most people won’t do it because it doesn’t provide much benefit.

Also, we should check for the connection status of a device by using the Network Information API that’s supported by many modern browsers.

Finally, we should check if a specific library or feature is available with Modernizr and Can I Use before using it.

Categories
JavaScript Best Practices

JavaScript Best Practices — Error Handling Techniques

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

In this article, we’ll look at how to handle errors in JavaScript programs.

Error-Handling Techniques

Error handling is something that we got to do.

If we let them slide, then our programs crash, see corrupted, and other bad things and users won’t be happy.

Therefore, we got to handle errors properly so that users won’t see nasty things happen.

Return a Neutral Value

Returning some error values that may be harmless is one way to go.

We may want to return some neutral value that we know to be benign when an error is encountered.

If we just want to stop the code from proceeding, then we can just return something and stop the function from running.

Substitute the Next Piece of Valid Data

We can also substitute error values with some valid data we get later.

For instance, if we’re measuring temperature periodically, then we can just wait for the next reading if the previous reading failed to get the temperature.

Return the Same Answer as the Previous Time

If we encounter an error once, we can just return the same answer as before if it’s valid.

If a water meter fails to measure water usage once, we can just wait for the next reading for the new value and just return the valid value that we have now.

Substitute the Closest Legal Value

We can also substitute the closet legal value.

If our water meter can only read a volume from 0 and up and we get a negative, then we substitute with 0.

Log a Warning Message to a File

Also, we can put an entry in the program’s log for the error that we encountered.

This can be used with other techniques outlined previously.

It helps us troubleshoot what’s later on if the error persists.

Return an Error Code

Returning an error code is another valid option for handling errors.

We can return an error code to let users and other parts of our program know that an error is encountered.

To return errors, we can set the value of a status variable, we can return the status code, or we can throw an exception with the status as the value.

Call an Error-Processing Function

If our code encounters an error, we can call an error processing function to handle the error gracefully.

They would log the error and log in so that we can debug gracefully.

This is also good because we can reuse it in other parts of our system.

Display an Error Message wherever the Error is Encountered

Errors can be displayed as they’re encountered if the error can be passed into the UI component of our system.

For instance, there’re many ways to display errors in client-side JavaScript like alert , dialog boxes, and things of that sort.

So we can like something like the following:

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

to display an error alert toa user.

Handle the Error in Whatever Way Works Best Locally

Errors can also be handled locally.

This carries great flexibility, but the performance of our system may suffer if we have lots of code to handle errors locally.

We also don’t want to spread that code everywhere to reduce the chance of code duplication.

Shut Down

Another thing we can do is to end our program when we encounter an error.

It’s not sure if we want to do this often as users may be surprised to see our program close when an error is encountered.

If we do this, we should make sure that pleasant user experience is maintained.

Robustness vs. Correctness

Robustness means that we do whatever it takes to keep our software operating.

Correctness means that we may sure everything is right before our program continues to run.

Consumer-facing apps favor robustness over correctness. If we’re writing JavaScript apps, it’s more likely than not that they aren’t safety-critical applications, so robustness is favored over correctness.

Conclusion

We can handle errors in many ways in our programs.

We can just take non-error values that were produced previously or wait for the next non-error value.

Also, we can throw exceptions, end our program, or display an error.

We got to think about the options that are most suitable for us.