Categories
JavaScript Best Practices

JavaScript Best Practices — Robust Code

=referral)

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Use Factory Functions

Factory functions are functions that aren’t a constructor function but return an object.

We can use this to return objects our way.

It’s useful if we don’t need inheritance while letting us create objects from a function.

For example, we can write:

function createDog(name) {
  const children = []

  return {
    addChild(dog) {
      children.push(dog)
    },
  }
}

Then we can create a dog object with:

const james = createFrog('james')

The object returns a function doesn’t reference this and it’s not an instance of any constructor.

Factory functions are reusable and can be composed.

Add Instance Methods on the prototype Property When Writing Constructors

If we want to add instance methods to a constructor function, we should add them to the prototype property of the function.

For instance, we can write:

function Dog(name) {
  this.name = name
}

Dog.prototype.speak = function() {
  console.log(this.name)
}

This is better than putting the method inside the constructor as the property of this because only one copy of the function will be shared between all instances.

This is different from:

function Dog(name) {
  this.name = name
  this.speak = function() {
    console.log(this.name)
  }
}

which creates a new speak method for instance of Dog .

Each copy takes more memory so it’s less efficient.

Use the type Property to Differentiate

We can differentiate something with the type property.

For instance, we can write:

function createSpecies(type, name, gender) {
  if (type === 'dog') {
    return createDog(name, gender);
  } else if (type === 'cat') {
    return createCat(name, gender);
  } else {
    throw new Error('invalid type');
  }
}

We can create different objects based on the value of type .

This is conventional so that it won’t confuse people.

Use TypeScript

TypeScript is an extension of JavaScript created by Microsoft to solve problems with restricting types in JavaScript code.

It’s very useful since it lets us restrict types and provide type definitions to do type checks and autocompletion.

It compiles down to JavaScript versions as early as ES3.

We can define interfaces that aren’t available in JavaScript.

This lets us restrict implementations to something standard.

Variables, parameters, and function return values can have their own types.

It also provides syntax which isn’t available in JavaScript like defining class variables outside class methods.

Therefore, TypeScript is definitely worth considering for moderately to large-sized projects.

Write Tests

Tests let us test our code automatically.

This makes testing much faster and less error-prone.

It also checks for regressions so it’s harder to make regressions when we change existing code.

There’re many things that let us do this easily.

Keep Functions as Simple as Possible

Simple functions are easy to test, change, and understand.

Therefore, we should keep them as simple as possible.

It’ll reduce lots of headaches.

Conclusion

We can write robust code with a few changes to our programming workflow.

We can write tests, use TypeScript, and add prototype methods.

Categories
JavaScript Best Practices

JavaScript Best Practices — More SEO

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Use Meta Robots Tags Carefully

We can add the noindex meta tag to prevent indexing.

We can add a static meta tag with:

<meta name="robots" content="noindex, nofollow">

And we can add one dynamically with:

fetch(`/api/items/${itemId}`)
  .then(response => response.json())
  .then(item=> {
    if (item.exists) {
      showItem(item);
    } else {
      const metaRobots = document.createElement('meta');
      metaRobots.name = 'robots';
      metaRobots.content = 'noindex';
      document.head.appendChild(metaRobots);
    }
  })

We create the meta element, add the attributes, and attach it to the head tag with document.head.appendChild .

If Google encounters noindex , then the JavaScript won’t run.

It’ll index the page if the meta tag with noindex is removed.

Use Long-Lived Caching

We should cache assets to reducing loading time.

This may lead to outdated resources being loaded.

To clear the cache when new content is deployed, we can add a unique fingerprint to the file name of them.

Use Structured Data

We can add structured data with a script tag:

<script type="application/ld+json">
  {
    "@context": "https://schema.org/",
    "@type": "Recipe",
    "name": "{{recipe_name}}",
    "image": [ "{{recipe_image}}" ],
    "author": {
      "@type": "Person",
      "name": "{{recipe_author}}"
    }
  }
</script>

This lets Google know the content of our page.

The type has to be set to “application/ld+json” for this to work.

Follow Best Practices for Web Components

We should make sure that Google sees the rendered content of our web component.

If it’s not visible, then Google can’t index it.

To test them, we can use the Mobile-Friendly Test or the URL Inspection Tool to look at the rendered HTML.

To make sure the light DOM and shadow DOM are both rendered, we should use the slot element:

<script>
  class MyComponent extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
    }

    connectedCallback() {
      let p = document.createElement('p');
      p.innerHTML = 'Hello World: <slot></slot>';
      this.shadowRoot.appendChild(p);
    }
  }

window.customElements.define('my-component', MyComponent);
</script>

<my-component>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  <p>Suspendisse vitae eleifend nulla.</p>
</my-component>

We have the slot in the shadow DOM to render the items between the tags.

Then everything will show.

Fix Images and Lazy-Loaded Content

If we have images, we should lazy load them so that they only show when they’re displayed to the user.

This reduces load time so users and Google can see the rendered content faster.

Title

We need a title on our pages.

We can add a title tag:

<head>
   <title>Page Title</title>
</head>

This lets Google identify the page easily.

Description

Also, we can add the meta description to describe our content to search engines.

For example, we can write:

<head>
   <meta name="description" content="description of the page" />
 </head>

Open Graph Image

To make our page show a featured image for social media, we need the open graph image tag.

For example, we can write:

<head>
   <meta property="og:image" content="https:/example.com/image.png" />
 </head>

Conclusion

We can improve our web app’s ranking with lazy loading, caching, and following best practices with web components.

Also, we should look add a few meta tags for search engines and social media.

Categories
JavaScript Best Practices

JavaScript Best Practices — Sugar and Placement

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Syntactic Sugar

Lots of recent changes in JavaScript are syntactic sugar for existing syntax.

We can make our lives easier with them.

There are several drop-in replacements that we can use with existing code.

let and const

We can use let and const to declare block-scoped variables.

They’re only available after they’re defined.

For example, we can write:

let x = 1;
const y = 2;

to make declare them.

Then we use them only after they’re declared.

const variables can’t be reassigned to a new value, but we can change things inside it if it’s an object.

Limiting the Scope of the Function

We can limit the scope of the function with arrow functions.

For example, instead of writing:

function foo(){}

which can be called anywhere even before it’s defined.

We write:

const foo = () => {}

If we don’t need the function to bind to its own this , then we definitely should use arrow functions.

They also can’t be called before they’re defined.

Class

Traditional constructor functions have been superseded by classes.

So instead of writing:

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

Animal.prototype.speak = function (){
  console.log(this.name);
}

We write:

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(this.name);
  }
};

Everything is in one place and it resembles actual classes even though it’s just syntactic sugar.

Naming Variables

We should name variables in a way that we can understand.

For instance, instead of writing:

let d ;
let elapsed;

We write:

let daysSinceUpdate

They should also be easy to pronounce so we won’t have a hard time processing them.

So we can write something like:

let firstName, lastName

Writing Functions

We should make functions that do one thing.

So we should write functions like:

const add = (a, b) => a + b;

It only adds.

Use Long, Descriptive Names

The names we have above are descriptive.

They communicate their intent.

For example, instead of writing:

function inv(user) {
  //...
}

We write:

function inviteUser(user) {
  //...
}

Avoid Long Argument List

We should avoid long lists of arguments in our code.

If we need lots of arguments, we should put them in an object.

The fewer arguments we have, the easier it is for us to handle.

For instance, instead of writing:

function getRegisteredUsers(fields, include, fromDate, toDate) {
  //...
}

We write:

function getRegisteredUsers({ fields, include, fromDate, toDate }) {
  //...
}

We have one object parameter instead of multiple parameters.

This way, we still have names for them but we can retrieved them in any order.

Reduce Side Effects

We shouldn’t have unnecessary side effects in our code.

For instance, instead of writing:

function addItemToCart (item, quantity = 1) {
  cart.set(item.id, quantity)
  return cart
}

We write:

function addItemToCart(cart, item, quantity = 1) {
  const cartCopy = { ...cart };
  cartCopy.set(item.id, quantity)
  return cartCopy;
}

Instead of accessing the cart which is somewhere outside the function, we make a copy of it and then do the manipulate it the way we want and return the copy.

Organize Functions in a File According to the Step-down Rule

Higher level fuicntions should be on top and lower level functions should be below them.

This way, the order that they’re read is logical.

For instance, instead of writing:

function getFullName(user) {
  return `${user.firstName} ${user.lastName}`
}

function renderEmail(user) {
  // ...
  const fullName = getFullName(user)
  return `Dear ${fullName}, ...`
}

We write:

function renderEmail(user) {
  // ...
  const fullName = getFullName(user)
  return `Dear ${fullName}, ...`
}

function getFullName(user) {
  return `${user.firstName} ${user.lastName}`
}

getFullName is a helper for renderEmail , so it should below it.

Conclusion

We can use some syntactic sugar to make our lives easier.

Also, we should place our code more carefully to make working with it easier later on.

Categories
JavaScript Best Practices

JavaScript Best Practices — SEO

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

SEO

SEO is making our website show up in earlier pages in search results.

This way, we get more reach.

With JavaScript-heavy websites, we need more work to improve our sites.

Google processes JavaScript with a crawler to index the page’s content.

It waits for the page to render and then index the content that’s rendered.

It does this if the page allows crawling.

To make rendering faster, we can do it on the server-side or pretender our content.

To do this easily, we can use frameworks like Next or Nuxt to do it.

Describe Our Page with Unique Titles and Snippets

We should add unique titles and snippets so that they can be identified easily.

Write Compatible Code

JavaScript offers many APIs for developers.

We should make sure that they aren’t deprecated and are compatible with most browsers to prevent browser-related issues.

Use Meaningful HTTP Status Codes

Google uses HTTP status codes to find out if some are wrong when crawling the page.

We should use meaning stratus codes to tell if Google whether a page should be crawled or not.

Common ones include 404 for not found and 401 or 403 for access denied.

301 or 302 are redirect responses.

500 series errors are errors on the server-side.

Avoid Soft 404 Errors in Single-Page Apps

We should redirect to a not found page when a 404 error is encountered when making HTTP requests.

Or we’ve to add <meta name=”robots” content=”noindex”> to error pages using JavaScript.

This way, Google knows that it’s a 404 page.

The redirect approach can be written as follows:

fetch(`/api/items/${itemId}`)
.then(response => response.json())
.then(item => {
  if (items.exists) {
    showItem(item);
  } else {
    window.location.href = '/not-found';
  }
})

We redirect to a not found when the item doesn’t exist.

Alternative, to create the meta element, we can do that with JavaScript:

fetch(`/api/items/${itemId}`)
  .then(response => response.json())
  .then(item=> {
    if (item.exists) {
      showItem(item);
    } else {
      const metaRobots = document.createElement('meta');
      metaRobots.name = 'robots';
      metaRobots.content = 'noindex';
      document.head.appendChild(metaRobots);
    }
  })

We create the meta element if the item isn’t found.

Use the History API instead of Fragments

The history API lets us go to various pages with it.

It implements routing between different views of our web app.

This makes sure Google can find links.

So instead of changing fragments to navigate like:

<nav>
  <ul>
    <li><a href="#/home">home</a></li>
    <li><a href="#/profile">profile</a></li>
  </ul>
</nav>

<script>
window.addEventListener('hashchange', () => {
  const pageToLoad = window.location.hash.slice(1);
  document.getElementById('placeholder').innerHTML = load(pageToLoad);
});
</script>

Instead, we use the history APU by writing:

<nav>
  <ul>
    <li><a href="#/home">home</a></li>
    <li><a href="#/profile">profile</a></li>
  </ul>
</nav>

<script>
function goToPage(event) {
  event.preventDefault();
  const hrefUrl = event.target.getAttribute('href');
  const pageToLoad = hrefUrl.slice(1);
  document.getElementById('placeholder').innerHTML = load(pageToLoad);
  window.history.pushState({}, window.title, hrefUrl)
}

document.querySelectorAll('a').forEach(link => link.addEventListener('click', goToPage));

</script>

We use the history API in the 2nd example to navigate.

We call preventDefault to prevent going to the href the default way so that we can navigate with JavaScript.

Next, we remove the leading slash with hrefUrl.slice(1);

Then we call pushState to navigate with the history API.

This way, the history is saved and we navigate.

Conclusion

We can navigate with the history API to update the browser history.

Also, we can look at various approaches to do SEO in our client-side web app.

Categories
JavaScript Best Practices

JavaScript Best Practices — Reducing Headaches

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Use === to Test Equality

=== is the best operator for testing equality.

The type and value are both checked to test for equality.

== converts the value according to its own rules before doing the comparison.

For example, we can write:

1 === '1'

returns false .

But:

1 == '1'

returns true because of the automatic data type conversion.

Likewise, we can use !== to check for inequality.

For example, we can write:

1 !== '1'

which returns true .

And:

1 != '1'

returns false for the same reason.

It’s easier to work with !== and === .

Include All Semicolons

We should put semicolons at the end of statements to avoid the JavaScript interpreter inserting them automatically for us.

This reduces the chance of errors since we’re in control.

Use ESLint

ESLint is the most popular linter for JavaScript.

We should use it to lint our code to catch errors and find formatting issues with our code.

It’ll pick up bad code so that we can fix them.

Also, it comes with many plugins for TypeScript and various style guides.

Use a Namespace

We should namespace our variables to avoid name collisions.

Instead of writing:

let price = 5;

We write:

let namespace = {};  
namespace.price = 5;

This way, we segregate the variable by putting the value in the namespace.

Avoid Using Eval

eval is definitely bad since it runs code from a string.

This is a security issue and prevents any kind of optimization from being done.

Debugging is also very hard since the code is in a string.

Use Decimals Cautiously

We should be careful with decimal operations.

For instance if we have:

0.1 + 0.2

it won’t return 0.3 as we expect.

Instead, we get:

0.30000000000000004

This is because floating-point numbers are represented with binary numbers.

The conversions between the 2 number systems mean that floating-point arithmetic won’t be exact.

We should, therefore, round them to the number of decimal places that we want to return.

Start Blocks on the Same Line

Blocks should start on the same line as the keyword.

For example, instead of writing:

if(environment === 'testing')   
{  
   console.log('testing');  
}   
else   
{  
   console.log('production');  
}

We should write:

if (environment === 'testing') {  
  console.log('testing');  
} else {  
  console.log('production');  
}

We can avoid errors in some cases avoid statements like:

return  
{  
   age: 15,  
   name: 'james'  
}

The object isn’t returned since it’s considered to be separate from the return statement.

Instead, we write:

return {  
  age: 15,  
  name: 'james'  
}

so we return the object.

Use Explicit Blocks

We should delimit blocks explicitly with curly braces.

This way, we won’t have any confusion on where a block starts or ends.

For example, instead of writing:

if (i > 3)  
  doWork();

We write:

if (i > 3) {  
  doWork();  
}

Now we won’t have any confusion with the blocks.

Conclusion

We can follow some basic best practices to reduce headaches.