Categories
JavaScript Best Practices

JavaScript Best Practices — Things we Comment On

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 the best practices for commenting code, including what we can comment on.

Commenting Paragraphs of Code

Paragraphs of code may receive comments to clarify them.

Anything that clarifies them and doesn’t repeat the code is good.

Write Comments at the Level of the Code’s Intent

Comments at the same level of the code’s intentions are good.

Therefore, whatever we’re commenting should be right before the code that we’re explaining.

Focus Documentation Efforts on the Code Itself

We should write self-documenting code all the time.

This means that the variable and constants names have to be meaningful.

Likewise, functions, classes, objects, and properties’ names also need to be descriptive.

For instance, we may write the following:

let numApples = 1;

Now we know that the variable above stores the number of apples.

We can do the same for everything else that we have to name.

Focus Paragraph Comments on the Why Rather than the How

We should focus on why we’re doing something in the comments rather than how.

How we do something is already explained by the code, so we don’t need to repeat that in our comments.

Use Comments to Prepare the Reader for what is to Follow

We may also want to tell readers what to expect in our code.

This way, they can read the comments and know what’s going on after the code.

Make Every Comment Count

Comments should be useful. Otherwise, they should be written.

We should focus more effort on making the cod readable rather than writing more comments.

Document Surprises

If we find things that aren’t obvious by looking at the code, then we should document them in comments.

This way, developers working on the code won’t fall into the trap.

Avoid Abbreviations

Not everyone understands abbreviations, so we shouldn’t use them.

Only the most commonly known ones may be used.

Differentiate Between Major and Minor Comments

We may also define different levels of comments.

To do that, we may want to do that with different symbols.

For instance, we can write:

// check strings that are to be deleted
// ... determine number of strings in the table

In the code above, we have the first line defined as major comments and second line as a minor comment as denoted by the ... symbol.

Comment Anything that gets Around an Error

We may want to comment on a workaround that we found in our code.

Also, we can do the same for undocumented features in a language or environment.

However, there shouldn’t be too many of these around.

Justify Violations of Good Programming Style

If we have to violate a good programming style, then we’ve to justify it.

It’ll make everyone know that isn’t being sloppy.

Don’t Comment Tricky Code, Rewrite it Instead

We shouldn’t write comments to explain tricky code.

If we can make them clearer by rewriting them, then we should do that.

This way, anyone else that’ll work on the code will thank us for making their lives easier.

Comment the Units of Numeric Data

If the units of numeric data aren’t completely clear, then we should make them clear by commenting on them.

We can also put the unit in the variable name rather than adding a comment, which is probably better since we won’t have to add the comment.

Comment the Range of Allowable Numeric Values

Restricting allowable values may be done with comments.

However, we should really do that with our code so that we won’t run into issues with processing invalid data.

Comment Coded Meanings

Since JavaScript doesn’t have an enum type, we may want to use comments to explain coded meanings.

We can use comments to explain what each value means.

Comment Limitations on Input Data

If there’re limitations on input data, then we may want to explain them in our comments.

However, we should also handle them in the code so that we make sure the limits are adhered to in the input data.

Conclusion

There’re many things that we may want to comment on, like input limitations and justify violations of good practices.

Paragraph comments should be used for justifying why rather than how we do things.

Categories
JavaScript Best Practices

JavaScript Best Practices — Loops

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 the best practices for writing loops.

When to Use a for Loop?

We can use the for loop for simple activities that don’t require loop controls inside the body.

So we can just let it run until the ending condition is met.

For instance, we can write:

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

Then we end the loop when i is 10.

If we don’t have a clear ending condition for the loop from the start, then we should use a while loop.

When to Use a for-of Loop?

The for-of loop is great for looping through any arrays or other iterable objects.

It’s great for arrays, sets, maps, Nodelists, and other objects with the Symbol.iterator method.

For instance, we can write:

const set = new Set([1, 2, 3]);
for (const s of set) {
  console.log(s);
}

to loop through a set.

When to Use a for-in Loop?

We should never use the for-in loop. It loops through object keys in an indeterminate order and also iterates through inherited properties that are enumerable.

Therefore, we should just avoid it and use the Object.keys method to get object keys as an array and loop through them.

Enter the Loop From One Location Only

We should only enter a loop from one location for consistency. Otherwise, confusion will result.

Put Initialization Code Directly Before the Loop

Loop initialization code should be put before the loop so that we make sure that we have all the data we need before the loop is run.

This way, we won’t forget to initialize the variables we need to run the loop later.

Use while( true ) for Infinite Loops

Sometimes we need to run infinite loops. We can run them with while(true) and then terminate the loop later inside the loop body.

We don’t want a loop that may have a condition becoming false sometimes.

for(;;) may also be an acceptable alternative.

Prefer for Loops When They’re Appropriate

for loops put all the loop control code in one place. This makes the code more readable.

It’s hard to forget to change the conditions if needed if they’re all in the same place rather than being scattered at different places.

Don’t Use a for Loop When a while Loop is More Appropriate

A while loop may be more appropriate if we need to control a loop more flexibly than a for loop can.

We don’t want to have for loops that have things that aren’t like:

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

Use { and } to Enclose the Statements in a Loop

If our loop only has one statement, then we may skip the curly braces for looping the wrap the loop body.

However, we should do it for clarity. For instance, instead of writing the following:

if (condition)
  doSomething();

We should write:

if (condition) {
  doSomething();
}

This way, we won’t have issues with confusing where the loop body starts and ends.

Avoid Empty Loops

Empty loops are useless. Therefore, there’s no point in creating them.

We should write code for something more useful.

Keep Loop Housekeeping Chores at Either the Beginning or the End of the Loop

We can add housekeeping chores at the beginning of the end of the loop.

For instance, we can write something like:

while (i < 10) {
  //...
  i++;
}

so that have the i++ at the end of the loop body.

This way, we know that all the changes are in one place.

Make Each Loop Perform Only One Function

Loops shouldn’t do multiple things. It just makes our code hard to read for them to do multiple things.

Therefore, we should have each loop do their own functionality.

Assure Ourselves that the Loop Ends

If our loop isn’t an infinite loop, then we should make sure that it ends.

We got to think about the normal cases, the endpoints, and exceptional cases.

Make sure the loop ends. Otherwise, it’ll probably crash our program.

Conclusion

We shouldn’t have any deceiving code in our loops.

Therefore, we should always add curly braces around the loop body.

Also, for loops are better in many cases since the conditions for starting and ending the loop are all at the top of the loop.

We should make sure that our loop ends in most cases.

Categories
JavaScript Best Practices

JavaScript Best Practices — Formatting

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 how to format file source code for readability.

Formatting

Formatting is important because we want to make everything look good.

The source code that looks good is more readable.

Braces

Braces should be used for all control structures.

This includes blocks, loops, and conditionals.

Even if they’re optional, we should still include it so that it’s clear where blocks start and end.

For instance, instead of writing:

if (condition) foo();

We write:

if (shortCondition()) {
  foo();
}

Nonempty Blocks

If we have nonempty blocks, then we should format it in a specific way.

We should have no line breaks before opening braces.

One line break should be after the opening brace.

Another line break before the closing brace.

And line break after the closing brace if a brave terminates a statement or the body of the function or class method, class statement, or class method.

For instance, we should write:

class Foo {
  constructor() {}

  method(foo) {
    //...
  }
}

We have methods that are separated with a blank line.

Empty Blocks

If we have empty blocks, then the braces should be on the same line.

For instance, we may write:

function nothing() {}

Block Indentation

2 spaces of indentation are the best spacing.

It minimizes typing and it’s obvious that there’s indentation.

Array Literals

Array literals may be optionally formatted like block-like constructs.

For instance, we can write:

const arr = [
  1,
  2,
];

or:

const arr = [1, 2];

Object Literals

Like array literals, we may indent properties of object literals like a block.

For instance, we can write:

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

Class Literals

We can write class literals with blocks inside indented.

There shouldn’t be semicolons after methods.

If we create a subclass, then we use the extends keyword.

For instance, we write:

class Foo {
  constructor() {
    this.x = 1;
  }

  foo() {
    return this.x;
  }
}

To assign it to something we write:

const Bar = class Foo {
  constructor() {
    this.x = 1;
  }

  foo() {
    return this.x;
  }
}

Function Expressions

If we have function expressions, then the body should be indented with 2 spaces more than the previous indentation depth.

For instance, we write:

function foo() {
  if (a1 === a2) {
    baz(a1);
  } else {
    bar(a2);
  }
}

or:

function foo() {
  bar()
    .baz()
    .then((result) => {
      if (result) {
        result.use();
      }
    })
}

Switch Statements

switch statements should be indented with 2 spaces.

And each case statement should also be indented with 2 spaces.

For instance, we write:

switch (val) {
  case 1:
    foo();
    break;

  case 2:
    bar();
    break;

  default:
    throw new Error('invalid value');
}

Statements

We should think about how to format statements for readability.

One Statement Per Line

To make them more readable, we should have one statement per line.

Always Add Semicolon

Each statement should end with a semicolon so that the JavaScript interpret won’t add them for us.

This way, they’re always where we want them.

Column Limit

Line length should be 100 characters or less to avoid overflowing the page.

We don’t want to scroll horizontally to read everything.

Line Wrapping

We should break statements so that each line is within the column limit.

Line Breaks

We should break lines at a higher syntactic level.

For instance, we can break lines at function calls, the dot notation, and after the opening parentheses.

A comma should stay attached to the token that precedes it.

For instance, we can write:

estimate = calc(estimate + x *
    estimate) /
  2;

We can also write:

const arr = [
  1,
  2
]

Conclusion

We should have line breaks to keep line lengths within 100 characters or less.

Braces should be formatted for readability.

Block bodies should have 2 spaces of indentation.

switch statements should have indentation inside it.

Line breaks should be added to break long lines, and also should be done at places like function calls, dots for accessing object properties, and after commas.

Categories
JavaScript Best Practices

JavaScript Best Practices— Currying and Partial Applications

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at how to curry and partially call functions.

Immediate Object Initialization

We can use IIFEs to initialize objects.

For instance, we can write the following code:

({
  hello() {
    return 'hello';
  },
  init() {
    console.log(this.hello());
  }
}).init();

Then we get 'hello' logged since we called the init method in the object.

We see that this.hello can be called as init is part of the same object.

We can either write:

({...}).init();

or:

({...}.init());

A curly brace that has methods inside is recognized as an object literal rather than a code block.

We can also use this to protect our variables from the global namespace just like we do with IIFEs.

Function Properties

Function can have properties just like any other objects.

We can use that to do caching by adding items to a property of it and using it later if needed.

For instance, we can write:

const foo = (param) => {
  if (!foo.cache[param]) {
    const result = {};
    // ...
    foo.cache[param] = result;
  }
  return foo.cache[param];
};

In the code above, we have the param parameter used as the key of the cache.

Then we can check if there’s anything cached with the key param in the cache property.

If there’s nothing cached, then we run the code inside the if block.

Otherwise, we return the cached value.

This is handy for storing computed values.

Configuration Objects

We can also create a configuration object for storing configuration for our software.

The configuration object can be used as a single parameter for an object rather than passing in multiple parameters.

Since the fewer parameters, the better. We can use that with the destructuring syntax to make our lives easier.

For instance, we can write:

const person = ({
  first,
  last
}) => {
  //,..
}

Then we can create an object as follows:

const conf = {
  first: "joe",
  last: "smith"
};

and use that to call the function:

person(conf);

Using configuration objects as parameters great because we don’t need to remember the order of the parameter.

Also, we can skip the optional parameters of safety.

This means easier maintenance and makes our code easier to read.

We don’t have to change the signature to add and remove parameters.

However, we do have to remember the names. Destructuring helps with that a lot though.

And property names can’t be minified.

Currying

Curring is when we return a function that has the arguments partially applied.

We don’t pass in all the arguments to the function we return.

For instance, we can write the following:

const curry = (firstName) => {
  return (lastName) => {
    return `${firstName} ${lastName}`;
  }
}

The code above takes a parameter, firstName in the outer function, then returns a function that takes lastName and a string that comes both together.

This lets us call 2 functions to complete the string.

The benefit of curry is that we minimize the number of changes to the program’s state.

Now we get 2 functions that have no side effects in the example above.

Partial Application

Partial application is the process of passing in fewer than the expected arguments to a function at one time.

For instance, if we have the following function:

const add = (x, y) => {
  return x + y;
}

Then we can apply one argument first:

const add = (x, y) => {
  return x + y;
}

Then we can partially call a function by using the Lodash partial method.

We can run:

const partialAdd = _.partial(add, 5);

To return a function with 5 applied as the first argument.

Then we can write:

const result = partialAdd(3);

to call the returned function with another argument.

Then we get that x is 5 and y is 3 so the sum is 8.

When to Use Currying

Currying is useful for situations when we call the same function and passing mostly the same parameters.

We can create a new function with the parameters passed in and we can call the function with the remaining parameters that they don’t share.

Conclusion

We can call methods in objects right after we define them.

This way, we only use them once and forget about them.

Currying and partial application of functions are great for calling the function with some arguments and return ones with those arguments applied.

Then we don’t have to call them again with those same arguments repeatedly.

Categories
JavaScript Best Practices

JavaScript Best Practices— JSONP and Deployment

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

The organize our code, we should use some basic design patterns. In the article, we’ll look at using JSONP and deployment strategies.

JSONP

JSONP is JSON with padding. We can use this instead of JSON to bypass the same-domain browser policy.

We should use this carefully since we’re loading data from 3rd party sites.

With JSONP, we have JSON wrapped in a function call.

The function name is provided with a JSONP request.

For instance, a JSONP request URL may look like:

http://example.org/getdata?callback=myHandler

where the myHandler is the name of the function on our app that’ll be called once the request is made.

The URL is loaded into the script element and then it’ll call myHandler once it’s done loading.

Deploying JavaScript

There are some things that we have consider when we deploy JavaScript code.

We’ve to be sure that we combine scripts so that they’re compacted.

Whitespaces, long names, etc. are all removed and replaced with shorter spaces and names.

Combining scripts is one more step before we deploy.

And we may lose some caching benefits. It’s not good to invalidate the cache for a small change in the bundle.

Also, we got to come with some versioning pattern for the bundle so we don’t lose track of them.

Minifying and Compressing

Minifying and compression code is important. The smaller the bundle, the less code that users have to download.

This means users can use our app faster.

That’s definitely good for users.

Minification and compression can give up to a 50% reduction in size compared to the original code.

We can compress our bundles with gzip compression. Our web server can be configured to serve gzipped bundles instead of plain text.

This will also give us a size reduction.

Compression will give us 70% smaller files on average.

With minification and compression together, users only download files that are 15% of the size of the original code.

Expires Header

Our cache should expire after a while. This way, users don’t have to refresh manually to see the latest changes.

We can change the expiry header to a value that we’re comfortable with.

If we set the cache long, then we’ve to rename the files every time we deploy so that the cache will be invalidated automatically.

Using a CDN

CDN stands for a content delivery network. These are paid services that let us distribute copies of our files in different data centers around the world.

This way, because of the proximity in location, the files will be served faster to users.

We can link to library files on CDNs and use them.

Loading Strategies

If we load files in our app, then we can load them in a few ways.

We can have inline scripts for simple scripts:

<script>  
  console.log("hello");  
</script>

Or we can load them from external sources:

<script src="external.js"></script>

There are some attributes that we can use to load scripts in different ways.

We can use the defer directive to make our script download in a way that doesn’t block our browser from running other code.

The Place of the Script Element

Script elements should be loaded as a bundle so that we don’t have lots of files that we have to load in order.

The more script tags we have, the more likely that we’ll screw up the order.

Therefore, we should make sure that we reduce the number of script tags by bundling.

HTTP Chunking

We can also chunk our pages so that they load in chunks.

We can move all the JavaScript to the head tag, while loading the rest of the body can load later.

Dynamic Script Elements

We can use the defer or async attributes in our script tags.

defer loads them asynchronously but in order.

async loads them asynchronously but may not be in order.

We can also create script tags dynamically by writing:

const script = document.createElement("script");  
script.src = "foo.js"; document.documentElement.firstChild.appendChild(script);

This will create a script tag that we can attach to the head tag or anywhere else.

Conclusion

We can reduce the size of our script bundles by minifying and compressing them.

This way, our code can be less than half of the size of the original code.

We can also use the defer and async directives to load script files.

And we can create scripts on the fly to defer loading.

JSONP lets us load JSON without the same domain restriction.