Categories
JavaScript Best Practices

JavaScript Best Practices- Modules

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices for importing items.

No Useless Path Segments

We shouldn’t have useless path segments in our imports.

It just makes the imports more complex than it should be.

For instance, if we have:

import "./../pages/about.js";

Then /.. and /pages cancel each other out.

Therefore, we don’t need the 2 segments.

Likewise, we shouldn’t have a slash at the end since it’s useless.

So we shouldn’t have:

import "./pages/";

Also, we don’t need an extension at the end of the module path.

So we shouldn’t write things like:

import "./pages/foo.js";

Instead, we write:

import "./../pages/about";

and:

import "./pages";

and:

import "./pages/foo";

to get rid of the useless parts.

No Relative Parent Imports

We should prevent imports to folders in a relative parent path.

So if we have something like:

import add from '../add';

Then we should put them in the same folder to simplify the folder structure.

We instead put them in the same folder and write:

import add from './add';

Bad Imports or Exports

There are import and export syntax that isn’t valid.

For instance, we can’t have more than one default export.

So we can write:

export default foo;
export default bar;

And we can’t have multiple named exports with the same name:

export const foo = function () { /*...*/ }
const bar = () => { /*...*/ }
export { bar as foo }

We should make sure that we only have one default export and one named export with a given name.

No Misleading Imports

We shouldn’r have misleading imports.

If we have a default import, then we should name our import with the same name as the module.

This may be done because we forgot the braces.

For instance, if we have:

import bar from './foo';

Then it may be a named import named bar but we forgot to include the braces.

We may also be misleading people intentionally or not with a default import that’s different from the name of the module file.

To make our lives easier, we should name our default imports with the same name as the module file:

import foo from './foo';

No Named as Default Member

We may have a module that has both a default export and one or more named exports.

For instance, we may have the following in foo.js :

export default 'foo';
export const bar = 123;

Then we can write:

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

This may be confusing to some people.

We may want to just have all named exports or only one default export and nothing else in our module.

No Deprecated Imports

If an export has been marked as @deprecated with JSDoc comments, we don’t want to import it since it’s going to be removed in the future.

If we used any deprecated exports in existing code, we should get rid of them as soon as we can.

Don’t Use Extraneous Packages

If we have packages that aren’t used anywhere in our code, then we shouldn’t import them.

So if we have:

const _ = require('lodash');

then we should use Lodsh somewhere.

Ideally, we only import the parts of Lodash that we need.

So we can write:

const { zip } = require('lodash');

Then we only use the zip method in our code instead of importing the whole module, which has hundreds of methods.

No Mutable Exports

We shouldn’t export mutable variables in our code.

For instance, we shouldn’t write things like:

export let count = 1;

let variables can be reassigned to anything.

So the exported value may be changed.

Instead, we use const :

export const count = 1

so that we can’t change our exported value.

Functions and classes can also be exported:

export function getNumbers() {}
export class Counter {}

Conclusion

We shouldn’t export mutable variables.

Also, we shouldn’t have useless path segments to make more imports more complex than they are.

We should make sure that our import/export syntax are valid.

Categories
JavaScript Best Practices

JavaScript Best Practices — Avoiding Bad and Outdated Features

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 way to define and work with functions.

Empty Catch Blocks

Empty catch blocks don’t anything. At least we should justify why the block is blank with a comment.

So we shouldn’t just leave it blank.

Switch Statements Fall-Throughs

switch blocks usually shouldn’t have fall-throughs. If we add them, we should say why we have them so that no one will be confused that it’s a mistake.

For instance, we should write:

switch (val) {  
  case 1:  
  case 2:  
    foo();  
    // fall through  
  case 3:  
    bar();  
    break;  
  default:  
    baz(val);  
}

default Case

We should have the default case.

This way, any value that doesn’t match any cases is processed.

The default statement must be last so that it’s only run when nothing else matches.

this

this should only be used in constructors and methods.

Arrow functions shouldn’t have their own this .

this should never refer to the global object.

Equality Checks

We should always use === or !== for equality and inequality comparisons respectively.

Otherwise, we may get data type coercion that we don’t want before checking comparison.

Exceptions Where Coercion is Desirable

We may want to use == for compare both null and undefined at the same time.

For instance, we can write:

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

with

We should never use the with keyword. It creates a new scope and causes confusion when we define a variable within in a with block.

It’s also not allowed in ES5 strict mode.

Dynamic Code Evaluation

We should never use eval or the Function constructor to run code or create new functions respectively.

Automatic Semicolon Insertion

Semicolons should be added by programmers rather than the JavaScript interpreter.

If we let the interpreter add it, then it may be in unexpected places.

Non-Standard Features

Features that aren’t in stage 4 in the W3C specification shouldn’t be used.

Also, we’ve to take into account what is supported by browsers before using our code.

Old features that have been removed like WeakMap.clear shouldn’t be used.

Wrapper Objects for Primitive Types

Never use new with primitive object wrappers such as Boolean, Number, String, Symbol .

We should use them without new .

This way, they return primitive values instead of objects.

For instance, we write:

const foo = `Boolean(1);`

instead of:

const foo = new `Boolean(1);`

Modifying Built-in Objects

Built-in objects should never be modified.

Objects like Array or String prototypes shouldn’t have new methods added to it.

We also shouldn’t modify methods in those objects.

Also, don’t add symbols to the global object.

If we want extra functionality, we should write our own code or use a standard polyfill.

Omitting () When Invoking a Constructor

We should always add parentheses when invoking a constructor.

For instance, we should write:

new Foo();

instead of:

new Foo;

Even though it’s allowed, we shouldn’t do it.

Conclusion

Just because some features are in JavaScript doesn’t mean we should use it.

Outdated features like eval and the Function constructor shouldn’t be used.

Wrapper objects shouldn’t be used to create primitive values.

Also, we should have a comment in empty catch blocks and switch case fallthroughs.

Categories
JavaScript Best Practices

JavaScript Best Practices — Objects and Variables

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

Use Bracket Notation When Accessing Properties with a Variable

We should use the bracket notation when we access properties with variables.

For instance, we can write:

const getVal = (prop) => {
  return foo[prop];
}

const bar = getProp('jedi');

The code above has a getVal function to pass prop into the object foo .

Use Exponentiation Operator When Calculating Exponentiations

Instead of using Math.pow , we should use ** for exponentiation.

For instance, we can write:

const result = 2 ** 5;

instead of writing:

const result = Math.pow(2, 5);

Variables

There are some things to think about when we’re declaring JavaScript variables.

Always Use let or const to Declare Variables

We should always use let or const to declare variables.

Otherwise, we end up with global variables.

For instance, instead of writing:

foo = 1;

or:

var foo = 1;

We write:

let foo = 1;

or:

const bar = 2;

const values can’t be reassigned but whatever is assigned is still mutable.

Use One const or let Declaration Per Variable or Assignment

We should declare variables all on their own line.

This way, we know the type of each variable exactly.

For instance, instead of writing:

const foo = 1, bar = 2;

We write:

const foo = 1;
const bar = 2;

Group All const and let Statements Together

Grouping them together makes reading them easier.

For instance, instead of writing:

let foo = 1;
const bar = 2;
let baz = 3;
const qux = 4;

We write:

let foo = 1;
let baz = 3;

const bar = 2;
const qux = 4;

Assign Variables Where we Need Them

We should assign variables close to where we need to use them.

This way, we don’t have to follow many lines of code to see where they’re used.

For instance, instead of writing:

let x = 1;
// 50 lines of code that doesn't change x ...
x = 2;

We write:

let x = 1;
// 5 lines of code that doesn't reference x...
x = 2;

Don’t Chain Variable Assignments

We shouldn’t chain variable assignments.

This is because we’ll create global variables out of it.

For instance, if we have:

let a = b = c = 2;

Then b and c are global variables.

So we should write:

let a = 2;
let b = a;
let c = a;

instead.

Avoid Using Increment or Decrement Operators

The increment and decrement operators both update the value and return them at the same time.

Depending on the placement of the operator, it may return the updated value or the old value.

If we place ++ or -- before the operand, then the new value is returned.

Otherwise, the old value is returned.

For instance, if we write:

let x = 1;
const y = ++x;

Then y is 2 since the updated value of x is returned.

On the other hand, if we write:

let x = 1;
const y = x++;

Then y is 1 since the old value of x is returned.

This also applies to --

Therefore, we should use the += and -= operators instead and make the increment and decrement operators a statement.

For example, we can write:

x += 1;

or:

x -= 1

instead.

Avoid Line Breaks Before or After = in an Assignment

We can use parentheses to wrap a long JavaScript expression so that we can see what we’re assigning to a variable clearly.

For instance, instead of writing:

const foo =
  longExpression;

We can write:

const foo = (
  longExpression
);

Conclusion

We should always use let or const to declare variables.

Also, we should use += or -= operators instead of ++ or -- for increment and decrement respectively.

If we have long expressions, we should wrap them in parentheses before assigning them.

We should use the dot notation as much as possible for accessing properties.

Categories
JavaScript Best Practices

JavaScript Best Practices — Functions Parameters, Errors, and Loops

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 way to define and work with functions.

We also look at throwing exceptions and loops.

Parameters and Return Types

We’ve to be careful with how we add parameters and return types in our functions.

Default Parameters

Default parameters have been a feature since ES6.

We can use them to define parameters with default values.

This way, we can skip them or set them to undefined and our parameter will still be set a value.

For instance, we can define a parameter with a default value by writing:

function foo(a, b = 2) {
  //...
}

b is a default parameter. Its default value is 2.

This way, when we call it as follows:

foo(1);

b will be set to 2.

Spread Operator

The spread operator should be used instead of using apply to call a function with multiple parameters using array entries.

It works with both traditional and arrow functions, unlike the apply method, which only works with traditional functions.

For instance, we can write:

fn(...foo, ...bar, ...baz);

Assuming that foo , bar , and baz are arrays, we can call them with all the entries from them as arguments.

Also, we can use it to retrieve arguments that haven’t been assigned to a parameter as an array.

For instance, if we have:

function fn(...elements) {}

Then if we call it by running:

fn(1, 2, 3);

Then elements would be [1, 2, 3] .

String Literals

It’s easy to step into traps when using JavaScript strings. Therefore, we should follow some best practices.

Use Single Quotes

Single quotes are easier to type, so we may consider using them so we don’t have to press the shift key to add double-quotes.

Ordinary strings shouldn’t span multiple lines.

Template Literals

If we’re thinking about concatenating strings, then we should use template literals instead.

It’s also great for creating multiline strings.

For instance, we can write:

function add(a, b) {
  return `${a} + ${b} = ${a + b}`;
}

Then we interpolate a and b in the string expression.

We should never concatenate strings again now that we have template strings.

No Line Continuations

The \ character has problems for forming multiline strings.

If there are any trailing whitespace, then we’ll get errors.

Therefore, we should use template strings to make long strings.

For instance, we can write:

const veryLongStr = 'very long string';
const longerStr = 'very very long string';
const longestStr = 'very very very long string';
const str = `${veryLongStr} ${longerStr} ${longestStr}`;

Now we embed the string variable in the template string to put them together.

Number Literals

Number literals may be specified in decimal, hex, octal, or binary.

We should never include a leading 0 unless it’s followed by x , o , or b so that they won’t be confused with the base.

Control Structures

There are many things to think about when creating control structures.

We should be aware of them.

For Loops

There are a few kinds of for loops. One is regular for loop. The other is for-in and for-of loops.

for-in shouldn’t be used to iterate over arrays. They should be used for iterating over the keys of objects.

We should use Object.prototype.hasOwnProperty should check if the property is an own property or an inherited one.

This way, we can check that a key isn’t inherited before working with them.

for-of is a loop that can loop through any iterable object, including arrays.

We can use the Object.keys method to get the own string keys of an object and loop through them with the for-of loop.

It’s better than for-in since Object.keys returns an array and loop through them.

Exceptions

Exceptions are an important part of JavaScript.

We should use them whenever exceptional cases occur.

To use it properly, we should throw Error or subclasses of Error .

Other objects shouldn’t be throw, or we’ll throw away useful information like line numbers of where the error occurred and stack traces.

Throwing exceptions is better than other ad-hoc error handling approaches.

Conclusion

We should throw exceptions to make errors more obvious.

When we use throw , we should throw an Error object or a subclass of it.

JavaScript has default parameters. We can use them to set the value of the parameter when the argument isn’t passed in.

for-of loop is versatile and simple, so it’s better to use it.

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.