Categories
JavaScript Best Practices

JavaScript Best Practices — Spaces, Commas, and Testing

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 properly space out the JavaScript code, adding commas in the right places, and testing.

Avoid Multiple Empty Lines

We shouldn’t have multiple empty lines in our code.

They’re useless and they take up space.

For instance, instead of writing:

const x = 1;


const y = 2;

We write:

const x = 1;
const y = 2;

Commas

Comma placement can also make our code easier or harder to read, so we should think about where they should be placed.

No Leading Commas

We should never have leading commas.

So instead of writing the following:

const story = [
    foo
  , bar
  , baz
];

We write:

const story = [
  foo, bar, baz
];

We may get syntax errors with leading commas.

Add Trailing Commas

Trailing commas make diffs cleaner and make rearranging the key-value pairs of an object easier.

So we should add them.

For instance, instead of writing:

const obj = {
  foo: 1
}

We write:

const obj = {
  foo: 1,
}

Semicolons

Semicolon placement is significant in JavaScript. Therefore, the meaning of the code may change depending on how they’re placed.

Add Semicolons Manually

We should add semicolons manually to avoid the JavaScript interpreter doing it for us.

If we let the interpreter do it, then it may be inserted in a place that we don’t expect.

Therefore, we should add them ourselves.

Instead of writing:

const arr = [1, 2, 3].map(x => x * 9)

We write:

const arr = [1, 2, 3].map(x => x * 9);

Type Casting & Coercion

Data type casting and coercion is often an issue with JavaScript when we let the interpreter do it.

Therefore, we should deal with data type conversions ourselves.

We should use the String , Number , and Boolean to do the conversions ourselves.

For instance, we can create a string by writing:

const str = String(1)

Note that there’s no new operator used. This will ensure that we return a string primitive value instead of an object.

Standard Library

There are some functions that are leftover from the past that we shouldn’t use anymore.

Use Number.isNaN Instead of isNaN

The global isNaN function does data type coercion before checking for NaN .

However, Number.isNaN checks for NaN as-is.

Therefore, we should use Number.isNaN instead of isNaN .

For instance, instead of writing:

isNaN('1.2')

We write:

Number.isNaN('1.6');

Number.isNaN(‘1.6’); returns false since it isn’t NaN .

Use Number.isFinite Instead isFinite

Like isNaN , isFinite tries to convert non-numbers to numbers before checking if it’s finite.

Therefore, instead of writing:

isFinite('5');

which returns true for the string, we should instead write:

Number.isFinite('5');

which returns false since '5' is a string.

Testing

We should write tests to make sure that we didn’t break anything.

To make writing tests easier, we should use pure functions, which minimizes mutations and side effects.

If we have too many mocks, then they make our tests weaker since they don’t test with real data.

Jest is a good test framework that we can use for writing and running tests.

100% test coverage is a great goal to strive or even though it might not be practical to reach it.

When we fix bugs, we should add some tests to make sure that our bug stays fixed.

Conclusion

We should add tests to our code to make sure that we didn’t break anything.

Spaces and commas should be added to improve readability.

Whenever we try to check for NaN or if a number if finite, use the methods in the Number object to do that.

Categories
JavaScript Best Practices

JavaScript Best Practices — Source Files and Modules

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 name our files and proper encoding for our files.

Also, we look at the right way to deal with modules.

File Names

File names should be all lower case and may include underscores or dashes.

We shouldn’t put any additional punctuation in our file names.

The file names should end in .js so that we know that it’s a JavaScript file.

For instance, foo.js is a good file name.

File Encoding

The encoding of the file should be UTF-8. This is standard across all platforms so that it won’t cause issues when we run them anywhere.

White Space Characters

The ASCII horizontal whitespace character should be the only one in source files.

Other whitespace characters should be escaped.

Tabs shouldn’t be used for indentation since they may be interpreted differently in different platforms.

However, tabs can be converted to spaces automatically.

Special Escape Sequences

Special escape sequences should have a corresponding numeric escape.

So \' is the same as \x27 , for example.

Non-ASCII Characters

We should use Unicode equivalent for non-ASCII characters in our code files.

Source File Structure

We may want to enforce some structure in our source files.

For instance, we may have JSDoc comments for documentation.

We may put them in folders so that there’s a clear hierarchy in our project structure.

ES Modules

Modules are used in most new JavaScript projects now.

Therefore, we should be mindful of some rules regarding modules.

Import Length

Length of import statements should be 100 characters or less so they won’t overflow the screen.

This way, no one has to scroll horizontally to read the whole line.

Import Paths

We should use the import sattement to import ES modules.

For instance, we can write:

import './foo';

or:

import * as foo from './foo.js';

or:

import { name } from './foo.js';

We don’t need the extension when importing files.

Importing the Same File Multiple Times

We shouldn’t import multiple members of the same file in different import statements.

For instance, instead of writing:

import { bar } from './foo';
import { baz } from './foo';

We write:

import { bar, baz } from './foo';

Naming Imports

We can name imports with the as keyword.

The name should be camelCase.

For instance, we write:

import * as fooBar from './fooBar';

Naming Default Imports

We can name default imports with camelCase also.

For instance, we can write:

import fooBar from './fooBar';

Naming Named Imports

Also, we can change the name of named imports so that we can use the name that we want to use.

For instance, we can write:

import { Cat as FatCat } from './animal';

for constructor imports or:

import { cat as fatCat } from './animal';

for other imports.

Named vs Default Exports

We can have named and default exports just like imports.

For instance, we can create a default export by writing:

export class Foo { ... }

and a named export by writing:

export { Foo }

Export Container Classes and Objects

We shouldn’t export container classes and objects

Therefore, instead of writing the following:

export class Container {
  static foo() {
    return 1;
  }
}

We write:

export function foo() {
  return 1;
}

export const FOO = 1;

Mutability of Exports

Don’t export variables that are mutable.

This means anything declared with let shouldn’t be exported.

For instance, instead of writing:

export let foo = 1;

We write:

export const foo = 1;

export from Statements

We should wrap export from statements so that it stays within 100 characters per line.

So we write:

export * from './foo';

or:

export { bar } from './another.js';

Circular Dependencies in ES modules

We shouldn’t create circular dependencies between ES6 modules, directly or indirectly.

For instance, we shouldn’t write:

b.js :

import './a';

and:

a.js

import './b';

Conclusion

We should be careful when working with file names and modules.

File names should be named in lowercase with underscore or dashes,

We should use modules and they shouldn’t have circular dependencies.

Code files should be UTF-8 encoded to avoid issues running them.

Categories
JavaScript Best Practices

JavaScript Best Practices — Modules, Arrays, and Objects

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 the best ways to work with modules, arrays, and objects.

If we have a Single Export, then we Should Use Default Export

If we have only one export from our module, then we should use a default export instead of named export.

It’s more readable and maintainable.

For instance, instead of writing:

export const foo = () => {};

We write:

const foo = () => {}
export default foo;

Put all imports Above Non-Import Statements

Putting all imports above non-import statements make them easier to read.

For example, we should write:

import { foo } from "./foo";

foo();

Multiline Imports Should be Indented Like Multiline Arrays and Object Literals

If we have lots of imports, we shouldn’t write:

import {foo, bar, baz, qux, longName} from 'foo';

Instead, we should write:

import {
  foo,
  bar,
  baz,
  qux,
  longName
} from "foo";

Don’t Use Webpack Loader Syntax in import Statements

If we use Webpack specific syntax for import things, then we’ll have issues when we try to switch to another module loader.

Instead, we should use standard syntax to avoid issues with Webpack specific syntax.

Instead of writing:

import barSass from 'css!sass!bar.scss';

We write:

import barCss from 'bar.css';

Don’t Add JavaScript File Extension to Imports

We don’t need to add the JavaScript file extension to imports.

It also creates problems when we change the extension,

Therefore, we shouldn’t include it.

For instance, we can write:

import { bar } from './foo.js';

Instead, we write:

import { bar } from './foo';

Iterators and Generators

Iterators and generators are JavaScript features new to ES6 that lets us return items sequentially.

Don’t Use Iterators to Manipulate Arrays

Instead of using loops, we should use array methods to manipulate arrays.

For instance, instead of writing:

let arr = [1, 2, 3];
let result = [];
for (const a of arr) {
  result.push(a ** a);
}

We write:

const arr = [1, 2, 3];
const result = arr.map(a => a ** a);

It’s shorter and no loops.

Another reason is that we don’t want to mutate array entries.

For instance, if we write:

let arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
  arr[i] = arr[i] ** arr[i];
}

Then we mutate each entry of arr as we loop through it.

If we can avoid mutation, then we should do that.

The following are some array methods that we can use to manipulate arrays:

  • map — map entries from one to another and return an array with the mapped entries
  • every — checks if each array entry meets some condition
  • find — returns the first entry of something that meets some condition
  • findIndex — returns the index of the first entry of something that meets some condition
  • reduce — combine array entries into a single value and return it
  • some — check if some array entries meet a given condition

We can also get object keys and values with these methods:

  • Object.keys — gets own string keys of objects
  • Object.values — gets own values of objects
  • Object.entries — gets own key-value pairs of objects

Don’t use Generators

If we’re targeting ES5 in our builds, then we shouldn’t use generators since they don’t transpile well.

Space Generators Definitions Properly

We should space the function definition as follows:

function* foo() {
  // ...
}

Object Properties

There’re a few things to be aware of if we access object properties.

Use Dot Notation When Accessing Properties

Dot notation should be used when we access object properties that are valid JavaScript identifers.

For instance, instead of writing:

const bar = foo['bar'];

We write:

const bar = foo.bar;

Conclusion

We should have a default export if we only export one member of a module.

Instead of using loops, we should use array methods to manipulate arrays.

If we need to manipulate object keys and values, we can get them with some Object static methods.

Also, use the dot notation as much as possible for accessing object properties.

Categories
JavaScript Best Practices

JavaScript Best Practices — Spaces

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 properly space out the JavaScript code.

Use Indentation for Making Long Method Chains

If we have method chains, then we should indent the method calls so that we see the chain more easily.

For example, instead of writing:

$('#foo').find('.selected').highlight().end().find('.bar').count;

We instead write:

$('#foo')
  .find('.selected')
  .highlight()
  .end()
  .find('.bar')
  .count;

Leave a Blank Line After Blocks and Before the Next Statements

If we have blocks followed by other statements, then we should follow that with an empty line so that we have the next statement separated from the block.

For instance, instead of writing:

if (foo) {
  return foo;
}
return baz;

We write:

if (foo) {
  return foo;
}

return baz;

It’s much clearer to separate them with an empty line.

Don’t Pad Blocks with Blank Lines

We shouldn’t pad blocks with empty lines.

This is because they don’t help with readability and takes up space.

For instance, instead of writing:

function foo() {

  console.log('bar');

}

We write:

function bar() {
  console.log(foo);
}

Don’t Use Multiple Blank Lines

We shouldn’t use multiple blank lines since they take up space and don’t help with improving readability.

For example, instead of writing:

const fullName = 'joe';


const email = 'joe@joe.com';

We write:

const fullName = 'joe';
const email = 'joe@joe.com';

Don’t Add Spaces Inside Parentheses

Spaces inside parentheses aren’t needed.

For instance, instead of writing:

function baz( foo ) {
  return foo;
}

We write:

function baz(foo) {
  return foo;
}

Don’t Add Spaces Inside Brackets

Likewise, we shouldn’t add spaces inside parentheses.

For instance, instead of writing:

const foo = [ 1, 2, 3 ];

We write:

const foo = [1, 2, 3];

If we access array entries or object properties, instead of writing:

foo[ 1 ]

We write:

foo[1]

And instead of writing:

bar[ 'baz' ]

We write:

bar['baz']

Add Spaces Inside Curly Braces

We should have spaces inside curly braces to improve readability.

For instance, instead of writing:

const foo = {baz: 1};

We should write:

const foo = { baz: 1 };

With the extra spaces, the code is much easier to read.

Avoid Lines of Code that are Longer Than 100 Characters

Lines of code that are longer than 100 characters may overflow people’s screens.

If that happens, they need to scroll horizontally to read the whole line.

To avoid that, we should our lines of code to 100 characters or less.

Consistent Spacing Inside an Open Block and Next Token on the Same Line

We should have consistent spacing between the open block and the next token.

For instance, instead of writing:

function foo() {return 'bar';}

We should write:

function foo() {
  return 'bar';
}

The extra spaces make our function easier to read.

Avoid Spaces Before Comma and Require a Space After Commas

We should have spaces before a comma and have one space after a comma.

For instance, instead of writing:

const arr = [1 , 2];

We should write:

const arr = [1, 2];

It’s much more readable.

Enforce Spacing Inside a Computed Property Bracket

Spacing should be enforced inside a computed property bracket.

For instance, instead of writing:

obj[foo ]

We write:

obj[foo]

No Spaces Between Function and its Invocation

We don’t need a space between a function and its invocation.

For example, instead of writing:

foo ();

or:

foo
()

We write:

foo();

Having Spacing Between Keys and Values in Object Literal Properties

We should have some spaces between keys and values in object literal properties.

For example, instead of writing:

const obj = {foo:1};

We should write:

const obj = {
  foo: 1
};

No Trailing Spaces at the End of Lines

A trailing space at the end of a line is useless.

Therefore, we should remove them if they are there.

We should also configure our text editor to remove them automatically.

Conclusion

We should have spacing in places where they’re needed like in keys-value pairs of objects.

However, we shouldn’t have spaces where they are needed like the end of lines, extra blank lines, and other things like that.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes and Modules

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 the best ways to work with classes and modules.

Add a toString Method

We can add a toString method to our class if we want.

However, we should make sure that it doesn’t commit any side effects.

For instance, we can write:

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

  toString() {
    return `Person - ${this.getName()}`;
  }
}

We have a toString method to return the string representation of our class instance.

We can Skip the constructor Method if we Don’t Want to Put Anything in it

We don’t need to add an explicit constructor method to a class.

For instance, instead of writing:

class Person {
  constructor() {}
  //...
}

We write:

class Person {
  //...
}

They’re the same.

If we have a subclass, instead of writing:

class Waiter extends Person {
  constructor(...args) {
    super(...args);
  }
  //...
}

We write:

class Waiter extends Person {
  //...
}

We don’t need a call to the parent constructor if we don’t do anything in the constructor that’s unique to the subclass.

No Duplicate Class Members

We shouldn’t declare duplicate class members.

This is because the value of the method will always be the last one.

If we have:

class Foo {
  bar() {
    return 1;
  }
  bar() {
    return 3;
  }
}

Then bar returns 3.

So we should just stick with one:

class Foo {
  bar() {
    return 1;
  }
}

2 or more members of the same name are redundant.

Class Methods Should use this or be Made into a Static Method

If our method doesn’t reference this , then it should be static.

For instance, instead of writing:

class Foo {
  bar() {
    console.log('bar');
  }
}

We should write:

class Foo {
  static bar() {
    console.log('bar');
  }
}

Modules

Since ES6 has modules, it’s time to use them to organize our code.

Always Use Standard Modules

Before JavaScript has modules as a standard, there were various module systems.

However, now that JavaScript has modules as a standard feature, it’s time to ditch non-standard module systems.

For instance, instead of writing:

const foo = require('./foo');

and:

module.exports = {
  foo: 'bar'
};

We should write:

import foo from './foo';
//...
export default bar;

or:

import { foo } from './foo';
//...
export default bar;

No Wildcard Imports

We shouldn’t use wildcard imports to import everything.

For instance instead of writing:

import * as foo  from './foo';

We write:

import foo from './foo';

or:

import { bar } from './foo';

Don’t Export Directly from an Import

We shouldn’t use export in the same line as we import.

It’s better to make the imports and exports clear and consistent.

For instance, instead of writing:

export { bar as default } from './foo';

to export bar in the same line as we import bar from foo , we write:

import { bar } from './foo';
export default bar;

Import a Path only in one Place

We should have multiple lines that import members from the same module.

For instance, instead of writing:

import bar from 'foo';
import { baz, qux } from 'foo';

We should write:

import bar, { baz, qux } from 'foo';

Don’t Export Mutable Variables

We shouldn’t export anything that’s mutable. This is because the last value will be exported.

For instance, if we have:

foo.js

let bar = 3;
export { bar };
bar = 5;

Then when we import bar :

import { bar } from "./foo";
console.log(bar);

bar is 5.

Even though the export is done before bar is set to 5, we still get that it’s 5 after we import.

Therefore, just export const members:

foo.js

const bar = 3;
export { bar };

Conclusion

We should never export mutable variables. This way, they can’t change after we export them.

Also, we should separate import and export statements so that it’s clear what we’re doing.

If we don’t want to put anything in the constructor, we can skip it.