Categories
JavaScript Best Practices

JavaScript Best Practices — Variables and Comparisons

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 variables and comparisons.

No Unused Variables

Unused variables are useless, so we shouldn’t include them.

For instance, if we have:

let x = 1;

but x isn’t used anywhere, then we should remove x .

Hoisting

var variables are hoisted and that tricks people and causes problems.

var Declarations are Hoisted to the Top, but their Value Isn’t

var declarations are hoisted but their value isn’t.

For instance, if we have:

console.log(x);
var x = 1;

then x is undefined .

But if we have:

var x = 1;
console.log(x);

Then x is 1.

This is a common source of confusion.

Therefore, it’s one reason that we shouldn’t use var to declare variables.

Use let or const instead to declare them.

Anonymous Function Expressions Hoist their Variable Name but Not Function Assignment

Like var variables, functions that are assigned to a var variable have the variable hoisted, but the function can only be used after it’s been assigned to the variable.

For instance, if we have:

foo();
var foo = () => {}

Then we get ‘Uncaught TypeError: foo is not a function’ since foo is undefined .

On the other hand, if we write:

var foo = () => {}
foo();

We can call foo .

Named Function Expressions Hoist the Variable but Bot the Function Name or Function Body

Likewise, if we have named functions the function itself isn’t hoisted, but the variable name is.

For instance, if we have:

foo();
var foo = function bar() {
  //...
}

Then we get the ‘Uncaught TypeError: foo is not a function’ error since foo is undefined .

But we can call foo by writing:

var foo = function bar() {
  //...
}
foo();

Function Declarations Hoist Their Name and Function Body

A function declaration is a named traditional function that isn’t assigned to a variable.

For instance, the following is a function declaration:

function foo() {
  //...
}

They can be called anywhere in the script, so:

foo();

function foo() {
  //...
}

and:

function foo() {
  //...
}

foo();

both call foo .

Comparison Operators & Equality

We should be careful when we use JavaScript operators, especially with the data type conversion that it does.

Use === and !== Over == and !=

We should use === or !== for equality or inequality comparison respectively since they don’t do any data type coercion before doing the comparisons.

For instance, instead of writing:

1 == '1'

We write:

1 === '1'

Likewise, instead of writing:

1 != '1'

We write:

1 !== '1'

This way, both operands have to have the same type and content for them to be considered equal.

Conditional Statements Like if Statements Evaluate their Expressions Using Coercion

Anything expression that we put between the parentheses is coerced into a boolean before evaluation.

Objects evaluate to true .

undefined and null evaluate to false .

Booleans evaluate to the value of the boolean.

Numbers evaluate to false if it’s +0, -0, NaN . Otherwise, it’s evaluated to true .

String evaluate to false if it’s an empty string and true otherwise.

We should keep these rules in mind when we have expressions in if .

For instance, if we have:

if ({ foo: 1 }){
  //...
}

Then { foo: 1 } will be coerced to true .

Use Shortcuts to Booleans but Explicit Comparison for Strings and Numbers

We don’t need to write things like:

if (valid === true){
  //...
}

Instead, we write:

if (valid){
  //...
}

But for strings or numbers, we do need to compare explicitly.

For example, we write:

if (value === 1){
  //...
}

Conclusion

If we have unused variables, we should remove them.

If we’re doing comparisons, we should use === or !== as much as possible.

When we compare with booleans, we don’t need to explicitly compare values with booleans.

Finally, we should be careful of function expressions and declarations which may trick us.

Categories
JavaScript Best Practices

JavaScript Best Practices — Type Conversions and Casing

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 cast types in JavaScript programs. Also, we look at the casing of identifiers.

Type Casting & Coercion

There are some rules that we should follow when we’re casting types.

Best Place to Perform Type Coercion

We should do type coercion at the beginning of a statement.

This way, we’ll know that we’re converting types right away.

No Wrappers

We should never use wrapper objects to convert types.

Instead, we should use factory functions.

For instance, instead of writing:

const totalScore = new String(score);

We write:

const totalScore = String(score);

This is because wrapper objects return an object instead of a primitive.

It’s not beneficial to us.

Wrapper objects are used by JavaScript to convert them to objects so that we can call its methods.

Use Number for Casting to a Number

Number is used for casting anything to a number.

For instance, we write:

const val = Number(inputValue);

This way, we get a number primitive value returned.

This is better than using a constructor or other functions.

For instance, writing:

const val = new Number(inputValue);

is bad since it returns an object rather than a number.

Using + as follows:

const val = +inputValue;

may confuse people that may not be familiar with the + operator.

If we use parseInt , we should pass in the base which we want to convert the number to.

For instance, we write:

const val = parseInt(inputValue, 10);

We put 10 as the 2nd argument to convert inputValue to a decimal number/

Don’t Use Boolean Constructor

Like numbers and strings, we should never use the Boolean constructor.

There’s no advantage to using it.

Instead, we use the Boolean factory function.

For instance, we write:

const hasAge = Boolean(3);

instead of writing:

const hasAge = new Boolean(3);

Naming Conventions

We shouldn’t use single letter names. They’re never that descriptive.

So instead of writing:

const q = () => {
  //...
}

We write:

const question = () => {
  //...
}

Use camelCase for Identifiers

We should use camelCase to name objects, properties, and constructor instances.

For example, we write:

const bigObject = {};

Use PascalCase for Constructors

PascalCase for used for naming constructors or classes.

For instance, we write:

class StudentUser {
  //...
}

or:

function StudentUser(name) {
  //...
}

No Leading or Trailing Underscores

There are no private variables in JavaScript constructors or classes, so we shouldn’t use underscores to indicate that they’re private.

So we shouldn’t write:

class StudentUser {
  constructor(name) {
    this._name = name;
  }
  //...
}

It just misleading to use underscores to indicate privacy when it’s not private.

Don’t Save Reference to this

We don’t need to save reference to this because we can use arrow functions to avoid doing that.

For instance, we can write:

function bar() {
  return () => {
    console.log(this);
  };
}

instead of:

function bar() {
  const self = this;
  return function () {
    console.log(self);
  };
}

It’s also shorter as an additional benefit.

Base Filename Should Match the Name of its Default Export

Default exports should match the name of the file that it’s exported from to eliminate any confusion.

For instance, we write:

import Foo from './Foo';

or:

import bar from './bar';

This way, we get no confusion.

Likewise, for exports, we write:

class Foo{
  // ...
}
export default Foo;

We make the case consistent to make them easier to read.

Use camelCase for Functions that are used as Default Export

Since functions are supposed to be camelCase, we should use name the default export the same way.

So we write:

function eatLunch() {
  // ...
}

export default eatLunch;

Conclusion

We should stick with standard JavaScript casing for functions, classes, and other identifiers.

This should also apply to imports and exports in modules.

To cast types, we should use factory functions that let return primitive values if we’re trying to convert to primitive values.

Categories
JavaScript Best Practices

JavaScript Best Practices — Names and Comments

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 practices for naming things in JavaScript projects.

We should look at the proper uses of JSDoc.

File and Module Names

Name files in lower camelCase.

For instance, we can name a file fooBar.js .

Class Names

Class names should be PacalCase. For instance, we can write:

class FooBar {
  //...
}

Method Names

Meth names should be written in lower camel case.

For instance, we should write:

class FooBas {
  //...
  doSomething() {
    //...
  }
}

If we have getters and setters, then we should make sure that we have names that reflect what they do.

For instance, we should name getters with names like getFoo . We can also name a method isFoo or hasFoo if they return a boolean.

If we have a setter, we should name them with names like setFoo .

Enum Names

Enum names should be UpperCamleCase.

For instance, we can define an enum by creating an object by writing:

const Unit = {
  METER: 'meter',
  FOOT: 'foot',
}

Unit is the enum name.

Also, we should use a singular noun.

Constant Names

Constant names should be named in CONSTANT_CASE.

The names upper case letters, with words separated by underscores.

Constants have const declaration, but not all module-local const s are constants.

const values can be mutable, just that they can’t be assigned a new value.

Local Aliases

We should create local aliases to help with readability.

For instance, if we want to reference an object with deeply nested properties, we can create an alias by writing:

const baz = obj.foo.bar.baz;

Now we can just reference baz instead of obj.foo.bar.baz .

It’s shorter and easier to read.

Aliases must be const so they can’t be reassigned to a new value.

Non-Constant Field Names

Field names, static or otherwise, should be written in camelCase.

For instance, we can have names like foo or numFoo for non-constant field names.

Local Variable Names

Local variable names are written in camelCase, except when we have constants.

Constant names in function scopes should also be written in camelCase.

camelCase should be used even if the variable holds a constructor.

JSDoc

We can write JSDoc comments to document our code.

For instance, we can write:

/**
 * Bunch of text,
 * and more text.
 * @param {number} num A number to do something to.
 */
function doSomething(num) {
  //...
}

The comment is above the function and it has a @param annotation for explaining the meaning of parameters.

We can use /** and */ on their own lines to extend the comment block.

There are many tools to extract comments from JSDoc.

Also, there are tools to perform code validation and optimization according to the comments.

Markdown

We write JSDoc comments in Markdown.

We follow the Markdown syntax when writing comments.

For instance, we write:

/**
 * Computes volume based on
 *
 *  - length
 *  - width
 *  - height
 */

We have - to denote a list entry for example.

JSDoc Tags

There are various tags that we can use with JSDoc.

@param is a commonly used tag as we saw above.

Other common tags include:

  • @constructor — mark function as a constructor
  • @enum — mark object as enum
  • @extends — mark class inheritance structure
  • @implements — mark the interface that’s implemented
  • @return — specify the return type of a function

There are many more tags that we can add to our comments to document our code.

Line Wrapping

Line-wrapped block tags should be indented 4 paces.

We shouldn’t align comment text horizontally.

For instance, we can write:

/**
 * @param {string} very very very very long
 *     description
 * @return {number} very very very very longtoo long
 *     description
 */
function bar(foo) {
  return 5;
};

Conclusion

We can comment on our code with JSDoc, which is based on Markdown.

Long lines should be wrapped and wrapped lines should be indented.

Also, the casing of names is pretty standard in JavaScript. We have camelCase for most names, and PascalCase and upper case for others.

Categories
JavaScript Best Practices

JavaScript Best Practices — Classes and Constructors

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 define and use classes in the best way.

Constructors

Constructors are optional in classes.

Subclass constructor must call super before setting any fields or referencing this .

Fields

Object fields should be set with values.

Fields should never be set on a class’s prototype .

For instance, we write:

class Foo {
  constructor() {
    this.bar_ = getBar();
    this.baz = getBaz();
  }
}

Properties should never be added or remove from an instance after the constructor is finished.

This will impede any optimization that may be done.

Rather, we should set properties that are set to undefined if we don’t want the value anymore.

Computed Properties

We should only use computed properties in our key is a symbol.

Also, we shouldn’t use built-in symbols unless we know we need to use them.

For instance, if we’re creating a class to create iterable objects, then we need to use Symbol.iterator .

Static Methods

We should add private module functions rather than static methods.

For instance, instead of writing:

class Bar {
  static foo() {}
  //...
}

We write:

const foo = () => {
  //...
}

class Bar {
  //...
}

Old-Style Class Declarations

ES6 classes are preferred to old-style class declarations.

If we create subclasses, then we should use class notation.

Don’t Manipulate prototypes Directly

With the class syntax, we shouldn’t manipulate prototypes directly.

This way, we can add methods and properties in a clearer fashion.

For instance, instead of writing:

function Foo() {
  //...
}

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

We write:

class Foo {
  bar() {
    //...
  }
}

Getters and Setters

We may use getters and setters. We can define them with the get and set keywords respectively.

However, we should never change variables in a getter.

For instance, we shouldn’t write:

class Foo {
  get next() { return this.newId++; }
}

Overriding toString

We can override toString as long as it doesn’t commit any side effects.

For instance, we just use them to return some values in string form:

class Foo {
  toString() {
    return `bar: ${this.bar}`;
  }
}

Functions

There are many things to think of when we’re declaring and using functions.

Top-Level Functions

We should use the export keyword to export top-level functions.

For instance, we can write:

export const foo = () => {
  //..
};

Nested Functions

Functions can have functions inside it.

If we have nested functions, we should assign it to a const variable.

For instance, we can write:

const bar = () => {
  const baz = () => {
    //...
  }
  //...
}

Arrow Functions

Arrow functions are great since it provides a concise syntax and it doesn’t bind to this .

We should always use arrow functions over function functions.

This way, we don’t have to use bind or const self = this; to set the value of this inside a function.

The left-hand side of the arrow may contain 0 or more parameters.

Parentheses around parameters are optional if there’s only a single non-destructured parameter.

For instance, we can write:

const add = (a, b) => a + Number(b);

Generators

Generator functions let us return generators so that we can retrieve items sequentially.

For instance, we can write:

function* gen() {
  yield 2;
  yield 3;
}

We can also call another generator inside a generator function with the yield* keyword.

For instance, we can write:

function* gen() {
  yield* gen1();
}

where gen1 is a generator function.

Conclusion

We should use the class syntax as much as possible.

If we want to export top-level functions in a module with the export keyword.

Also, we should assign nested functions to const .

We can also override toString in our classes.

Categories
JavaScript Best Practices

JavaScript Best Practices — Booleans

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 boolean expressions with JavaScript.

Use Decision Tables to Replace Complicated Conditions

If we have lots of boolean expressions to check for, we should use a lookup table to let us get the value from it in a shorter way.

For instance, instead of writing:

const findDayOfWeek = (val) => {
  if (val === 0) {
    return 'Sunday'
  } else if (val === 1) {
    return 'Monday'
  } else if (val === 2) {
    return 'Tuesday'
  } else if (val === 3) {
    return 'Wednesday'
  } else if (val === 4) {
    return 'Thursday'
  } else if (val === 5) {
    return 'Friday'
  } else if (val === 6) {
    return 'Saturday'
  }
}

we instead write:

const dayMap = {
  0: 'Sunday',
  1: 'Monday',
  2: 'Tuesday',
  3: 'Wednesday',
  4: 'Thursday',
  5: 'Friday',
  6: 'Saturday',
}

As we can see, it’s much shorter, and they do the same thing.

Forming Boolean Expressions Positively

Boolean expressions should be formed positively so that we don’t have to deal with double negatives.

For instance, if we have the following:

if (!ok) {
  // do something
} else {
  // do something else
}

We should write:

if (ok) {
  // do something else
} else {
  // do something
}

We flip the cases around.

They’re both the same, but the second one is much easier to read.

Apply DeMorgan’s Theorems to Simplify Boolean Tests with Negatives

DeMorgan’s Theorem states that the following:

!(a && b)

is the same as:

!a || !b

and:

!(a || b)

is the same as:

!a && !b

Therefore, we can use it to simplify these kinds of boolean expressions by applying the negation to all expressions.

Using Parentheses to Clarify Boolean Expressions

If we have complex boolean expressions, then we should clarify them by grouping them with parentheses.

So instead of writing expressions like:

a === b > c === d

We write:

(a === b) > (c === d)

This way, we know how they’re going to be evaluated when we run the code.

Short-Circuit Evaluation

JavaScript evaluates boolean in a short-circuit manner. This means that it’ll stop at the first expression evaluates to true if it’s an or expression.

If it’s an and expression, it stops at the first false expression.

Expressions are checked from left to right.

Therefore, we should make sure that we have expressions in the right order.

For instance, if we have:

if (((numerator / denominator) < MAX_VALUE) && (denominator !== 0)) {
  //...
}

Then we can potentially be dividing by 0 in the first boolean expression since the denominator !== 0 check comes after the (numerator / denominator) < MAX_VALUE check.

Therefore, we should reverse that so we check that the denominator isn’t 0 before we divide numerator by denominator .

We should make sure the order of the expressions actually makes sense.

Writing Numeric Expressions in Number-Line Order

Writing expressions in number line order makes them easier to read.

Our brains expect numbers to go in order, so we should write our expressions in such order.

For instance, we can write the following:

1 < val && val < 10

instead of:

val < 10 && 1 < val

It just makes more sense to have them in number line order.

Compare Logical Variables Implicitly

We don’t need to compare boolean variables directly with true or false .

For instance, we write:

while (!done) {
  //...
}

instead of:

while (!done === true) {
  //...
}

As the part after !done is redundant.

Compare Numbers to 0

If we compare numbers to 0, then we should write that out explicitly.

For instance, we should write:

while (amount !== 0) {
  //...
}

Also, make sure that we’re using !== or === to compare inequality and equality respectively.

Put Constants on the Left Side of Comparisons

Putting constants on the left side of comparisons lets us avoid accidentally assigning values to them.

For instance, instead of writing:

if (MIN_D0NUTS === i) {
  //...
}

is better than:

if (i === MIN_D0NUTS) {
  //...
}

As it avoids any accidental assignments.

Conclusion

We can make our lives easier by avoiding double negatives and nested boolean expressions.

Also, we should make sure that we have expressions in the correct order so that preconditions are evaluated first.