Categories
JavaScript Best Practices

JavaScript Best Practices — Bundles, Linting, and Objects

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

In this article, we’ll look at some best practices when developing in JavaScript, including bundling, linting, and object creation.

Minify in Production

Before releasing our code to production, we got to minify it so that users won’t have to spend too much time downloading our JS app.

Frameworks have built-in programs to bundle and minify our code.

Also, we can create our own bundler with packages like Grunt and some plugins.

Webpack and Parcel are also popular module bundlers.

Bundling strips white spaces, newlines, and comments.

They also rename variables to shorter names when it’s safe to do so.

Minifiers can only rename local variables, so that’s another reason we shouldn’t use global variables.

We should let the minifier do the minification.

Our code should have the descriptive names and let the minifiers strip characters as they see fit.

Run ESLint

ESLint checks for syntax errors and bad practices and either tell us to correct it or it can correct some of them automatically.

For instance, it’ll detect unreachable code, any old syntax that can be replaced with better new syntax, etc.

It’ll also detect bad constructs like eval , with , and void , which should never use.

Improperly escaped characters in regexes will also be detected to prevent potential security issues.

It’s available as a Node package and frameworks tools probably already have them included.

Therefore, we should use them to catch anything that we shouldn’t have.

Object Literal

Object literals are a simple way to define objects in JavaScript.

We can define objects in a one-off manner.

They contain key-value pairs, where the value may also be an object.

For instance, we can define an object literal as follows:

const cat = {};
cat.name = "joe";

or we can write:

const cat = {
  name: "joe"
};

We can also add methods to an object as follows:

const cat = {
  name: "joe",
  getName() {
    return cat.name;
  }
};

We can use the delete operator to remove a property.

For instance, we can write:

delete cat.name;

An object literal will have inherited properties from the Object prototype.

Therefore, even an empty object isn’t actually empty.

To write an object literal, we start with curly braces and delimit each property with commas.

Property names and values should be separated with a colon.

Objects from a Constructor

We can also create objects from a constructor.

Constructors are functions that have their own instance variables and prototype.

For instance, we can create a new Date instance by writing:

const date = new Date();

We used the new operator on the constructor to make it return a new Date instance.

Object Constructor

The Object constructor isn’t very useful for us.

It’s just a long way to create an object.

Also, we can pass in whatever value we want to create an object of our choice, which is confusing.

If we pass in a string, we get a string returned. So if we have:

const str = new Object("foo");

we get that str is a string.

It has access to all the string methods, but the type according to typeof is 'object' .

That’s no good since it’s confusing.

Therefore, we should just stick to literals as much as possible.

Otherwise, we’ll be confused about the type and the content.

Photo by Jerry Wang on Unsplash

Custom Constructor Functions and Classes

We can define a constructor function as follows:

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

Person.prototype.greet = function() {
  return `hi ${this.name}`;
}

Then we can write using the new operator to invoke it to create a new Person instance as follows:

const person = new Person('joe');
person.greet();

When we use the new operator, an empty object is created and referenced by this inheriting the prototype of the constructor.

Properties and methods are added to the empty object.

The created object referenced by this is returned.

We can also rewrite that with the class syntax by writing:

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

  greet() {
    return `hi ${this.name}`;
  }
}

Both are exactly the same. Just that the members are in different places.

Conclusion

Minifying is a must in production so that users won’t have to download as much data.

Also, linting helps us catch syntax errors and bad practices.

Finally, classes and constructors are the same in JavaScript.

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.