Categories
JavaScript Best Practices

JavaScript Best Practices — Spacing, Lines, and Call

Spread the love

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

Named Function Expressions

Names in function expressions aren’t very useful.

If we set the function to a variable, we can’t use it with that name.

We also don’t need it much in IIFEs.

So we can write:

Foo.prototype.bar = function() {};

instead of:

Foo.prototype.bar = function bar() {};

Also, we can write:

(function() {
    // ...
}())

instead of:

(function bar() {
    // ...
}())

Unless we need the name inside the function, it’s not that useful.

Consistent Use of Either Function Declarations or Expressions

We can keep to one function style for consistency.

So either we stick with:

function doWork() {
  // ...
}

or:

const doWork = function() {
  // ...
};

Arrow functions can only use the second style.

Line Breaks Between Arguments of a Function Call

We only need line breaks for arguments and function calls are only needed for long expressions or argument lists.

For instance, we can write:

foo(
  "foo",
  "bar",
  "three"
);

or:

foo("one")

We just keep line length around 100 characters or less.

Consistent Line Breaks Inside Function Parentheses

We should have consistent line breaks inside function parentheses.

For instance, we can write:

function foo(
  bar,
  baz
) {}

or:

const foo = function(
  bar, baz
) {};

For short functions, we can keep the signature all in one line.

Other than that, we break it into multiple lines.

Spacing Around the * in Generator Functions

We should keep the spacing around the * in generator functions.

For instance, we can write:

function* generator() {
  yield "foo";
  yield "bar";
}

or:

function * generator() {
  yield "foo";
  yield "bar";
}

or:

function *generator() {
  yield "foo";
  yield "bar";
}

We just have to stick with one.

Return Statement Should be Present in Property Getters

The whole point of getters is to return something.

Therefore, we should make sure that’s done in getters.

For instance, we should write:

const obj = {
  get name() {
    return "james";
  }
};

or:

Object.defineProperty(obj, "age", {
   get() {
     return 100;
   }
 });

instead of:

const obj = {
  get name() {
    return;
  }
};

or:

Object.defineProperty(obj, "age", {
   get() {

   }
 });

or:

class P {
  get name() {

  }
}

Put require() on the Top-Level Module Scope

We should put our require calls at the top level of the module scope.

For instance, we should write:

const fs = require("fs");

instead of:

function foo() {
  if (condition) {
    const fs = require("fs");
  }
}

Dynamically requiring things is confusing,

So we shouldn’t do it.

Require Grouped Accessor Pairs in Object Literals and Classes

Having setters that don’t have getters isn’t very useful since we can’t get the value that’s set.

Therefore, we should have getters for any setters.

For instance, we should write:

const obj = {
  get a() {
    return this.val;
  },
  set a(value) {
    this.val = value;
  },
  b: 1
};

instead of:

const obj = {
  set a(value) {
    this.val = value;
  },
  b: 1
};

We can get the value of a in obj with the getter.

Guarding for for-in

We should check for inherited properties if we loop with for-in since it enumerates inherited properties.

Instead of writing:

for (key in foo) {
  doWork(key);
}

We write:

for (key in foo) {
  if (Object.prototype.hasOwnProperty.call(foo, key)) {
    doWork(key);
  }
}

or:

for (key in foo) {
  if ({}.hasOwnProperty.call(foo, key)) {
    doSomething(key);
  }
}

We use Object.prototype.hasOwnProperty.call or {}.hasOwnProperty.call instead of foo.hasOwnProperty so that it still works if hasOwnProperty has been overwritten.

hasOwnProperty checks if a property is a noninherited property.

Callback Error Handling

We should check for errors in callbacks if they exist.

For instance, we can write:

function foo(err, data) {
  if (err) {
    console.log(err.stack);
  }
  doWork();
}

If it’s a Node style callback, err will be populated with the error, which we should handle in case it’s thrown.

Conclusion

We should handle errors in callback.

Naming function expressions is redundant.

Line breaks and spacing should be consistent.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *