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.

Categories
JavaScript Best Practices

JavaScript Best Practices — Arrow Functions and Classes

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 arrow functions and classes.

Use Arrow Functions for Anonymous Functions

Arrow functions should be used for anonymous functions.

This way, we don’t have to deal with different values of this , and it’s shorter.

For instance, instead of writing:

[1, 2, 3].map(function (x) {
  return x + 1;
});

We write:

[1, 2, 3].map((x) => x + 1);

It’s much shorter and the value of this is the same as the outside.

Remove Curly Braces for Arrow Functions that are one Statement Long

We don’t need curly braces for arrow functions that are one statement long.

For instance, we can write:

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

The return is implicit so we also don’t need to write return .

If an Expression Spans Multiple Lines, Wrap it in Parentheses

If we have an arrow function that’s one statement long, then we’ve to wrap it in parentheses so that we can return the whole expression.

For instance, we can write:

[1, 2, 3].map((x) => ({
  [x]: x
}));

This way, we map each array entry with the key and value set to the array entry’s value.

We can do that for any long expressions.

Include Parentheses Around Arguments for Clarity and Consistency

Adding parentheses around arguments make our arrow function signature clearer.

It’s also consistent with arrow functions that make no arguments or multiple arguments.

For instance, we write:

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

instead of:

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

Avoid Confusing Arrow Function Syntax with Comparison Operators

We should put parentheses around comparison expressions so that we know that they’re comparison expressions.

For instance, instead of writing:

[1, 2, 3].map((x) => x <= 2);

We write:

[1, 2, 3].map((x) => (x <= 2));

As we can see omitting the parentheses make our arrow function much harder to read.

Classes & Constructors

We should embrace modern syntax instead of using old syntax.

Use class Syntax Instead of prototype

Using the class syntax makes things clearer than manipulating prototype properties.

Prototypes are something that many people are confused about.

To add instance methods to a constructor, instead of writing:

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

Person.prototype.getName = function() {
  return this.name;
}

We write:

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

  getName() {
    return this.name;
  }
}

Instead of adding methods to the prototype property, we should put methods inside a class instead.

Instance variables should be initialized in the constructor method instead of a constructor function.

After a class is declared with the given, we can’t declare another one with the same name.

That isn’t the case with constructors.

Use extends for Inheritance

extends with the class syntax makes inheritance much easier.

Also, instanceof will work properly.

For instance, instead of writing:

function Animal(name) {
  this.name = name;
}
Animal.prototype.getName = function() {
  return this.name;
}

function Dog(name) {
  Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Animal;

We should write:

class Animal {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }
}

Now when we create a Dog instance, we can use instanceof to check if it’s an instance of Animal .

We can write:

const dog = new Dog();
console.log(dog instanceof Animal)

to see that the console log shows true so inheritance is done correctly.

The constructor function syntax gives us the same result but the process is more complex and error-prone.

Methods can Return this to Help with Method Chaining

In classes, we can have methods that return this to let us chain them.

For instance, we can write:

class Cube {
  setLength(val) {
    this.length = val;
    return this;
  }

  setWidth(val) {
    this.width = val;
    return this;
  }

  setHeight(val) {
    this.height = val;
    return this;
  }
}

Then we can chain methods of the Cube instance by writing:

const cube = new Cube()
  .setHeight(10)
  .setWidth(20)
  .setLength(30)

And cube is {height: 10, width: 20, length: 30}

Conclusion

The class syntax and arrow functions are 2 of the best features of JavaScript, so we should use them as much as possible.

Categories
JavaScript Best Practices

JavaScript Best Practices — Comments and 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 the best ways to work with comments and spaces.

Comments

We should be careful when writing JavaScript. We want them to be readable like anything else.

Use /** ... */ for Multiline Comments

The /** ... */ block denotes that a multiline comment inside it.

It’s better than // since we can write our comments in one block.

For example, instead of writing:

// foo
// bar

We can write:

/**
 * foo
 * bar
 */

Use // for Single Line Comments

// is great for single-line comments. We should put a single line above the subject of the comment.

And an empty line should be added before the comment.

For instance, we should write:

function getType() {
  // returns type
}

or:

function getType() {
  console.log('get type');

// returns type
}

Prefix FIXME and TODO to Label Code that Needs Changing Later

We can change FIXME and TODO to label code that needs changing later.

This way, we won’t forget about them.

We just have to remember to remove comments after we’re done.

For instance, we can write:

// TODO - clean up code

If our code has Problems, then use // FIXME to Mark Them

For instance, we can write:

// FIXME - remove global variable

so that we can fix that.

Use // TODO to Mark Solutions to a Problem

If we have a solution that we don’t have time to implement yet but we have an idea of how to do it, we should use // TODO to mark that.

For instance, we can write:

// TODO - make total configurable

to mark the solution to some problem that hasn’t been implemented yet.

Whitespace

Spaces make things easier to read, so we should add them consistently.

Use Soft Tabs Set to 2 Spaces

Spaces are consistent among operating systems, so they’re better than tabs.

2 spaces make things readable without too much typing.

So instead of writing:

function foo (){
 let x;
}

We write:

function foo (){
  let x;
}

We can also configure our text editor to convert 1 tab to 2 spaces automatically to save typing.

Place 1 Space Before Leading Brace

We should place 1 space before a leading brace so that we can read our functions easier.

For instance, instead of writing:

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

We write:

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

Likewise, with arrow functions, instead of writing:

const foo = () =>{
  console.log('foo');
}

We write:

const foo = () => {
  console.log('foo');
}

With function calls, instead of writing:

person.set('attrs',{
  age: 1,
  gender: 'male'
});

We write:

person.set('attrs', {
  age: 1,
  gender: 'male'
});

Just one space makes things more readable.

Place 1 Space Before the Opening Parenthesis in Control Statements

If we’re writing conditional statements or loops, we should have 1 space before the opening parenthesis.

And we should have no pace between the argument list and the function names in function calls and declarations.

For instance, instead of writing:

if(foo) {
  bar ();
}

We should write:

if (foo) {
  bar();
}

Likewise, for loops, instead of writing:

while(foo) {
  bar();
}

We write:

while (foo) {
  bar();
}

For function definitions, instead of writing:

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

We write:

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

The spacing makes our blocks more readable.

Set Off Operators with Spaces

We should have spaces between operands and operators to improve readability.

For instance, instead of writing:

const x=y+1;

We write:

const x = y + 1;

End File with a Single New Line Character

We should always end a file with a single newline character.

This way, they can always be concatenated properly by programs that do that if needed.

For instance, instead of writing:

index.js

const x = 1;

We write:

index.js

const x = 1;


Conclusion

Spacing is important. Therefore, we should have them in places where they make sense.

If we write comments, we should denote multiline comments in a different way than single-line comments.

We may also use comments to denote todo items and things that need fixing.

Newline character should be at the end of file so that it can be concatenated properly with other files.