Categories
JavaScript Best Practices

JavaScript Best Practices — Variables and Strings

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.

Variable and Function Naming Conventions

We should have variable and function names that are clear.

In JavaScript, names are usually camelCase except for constants and constructors.

For example, we can write:

const firstName = 'james';

We name constants with:

const MAX_RETRIES = 50;

And we name constructors and classes with PascalCase:

class Dog {}

Using Global Variables

We want to avoid creating global variables.

They’re accessible everywhere so they can be modified by anything.

That leads to hard to trace issues.

So we should share variables with other ways like modules.

Functions

Functions should do one thing at a time.

If it has to do more, then we should separate it into a helper function.

Progressive Enhancement

We shouldn’t assume that everyone has JavaScript enabled.

To display something to users that don’t have JavaScript enabled, we can put stuff in the noscript tag for them.

Don’t Change Variable Type After Init

To make our lives easier, we shouldn’t change the variable type after a variable is initialized.

For example, we shouldn’t write:

let status = "Success";
status = 1;

Changing types makes debugging harder.

Instead, we can assign values with different types to different variables.

Use Inline Commenting

We can add inline commenting in our code for comments.

We should keep them short and to a single line.

For example, we can write:

addtoCart(order) // add to cart

Control Variable Scope

To control variable scope, we can use let and const .

They’re all block-scoped so there won’t be any confusion as to where they’ll be available.

For example, we can write:

for (let i = 0; i < 10; i++) {
  console.log(i);
}

We use let to define i so that it’s only available within the loop.

JavaScript is Case-Sensitive

We should remember that JavaScript is a case sensitive language.

So foo is different from Foo .

This applies to everything.

So we should make sure our code has the right case.

Traverse an Array From the Right End

We can traverse an array from the right end with the array instance’s slice method.

For instance:

const arr = ['a', 'b', 'c', 'd', 'e', 'f'];
console.log(arr.slice(-1));

returns ['f'] and:

console.log(arr.slice(-2));

returns ['e', 'f'] and so on.

slice doesn’t modify the array is called on.

Substitute Loop with map()

If we want to map array entries from one thing to another, we don’t have to use a loop.

Instead, we use the map instance method.

For example, we can write:

const itemsIds = items.map((item) => {
  return item.id;
});

to get the IDs from items in the items array.

Replace All Occurrences of a String

We can call replace with the g flag to replace all occurrences of a string.

For example, we can write:

const str = "foo foo bar baz";
console.log(str.replace(/foo/g, "qux"));

Then ‘qux qux bar baz’ is returned.

Conclusion

There’re many tricks we should be aware of to improve our JavaScript code.

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.