Categories
JavaScript Best Practices

JavaScript Best Practices — Arrays and Arrow Functions

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 working with arrays and arrow functions.

Use map to Map Items

The map method is great for mapping array entries from the original to something else.

This way, we don’t need to use loops to do it.

So instead of writing:

for (const a of arr) {
  cubed.push(a ** 3);
}

We write:

const cubed = arr.map(a => a ** 3);

This is much shorter and clean.

No Useless this Argument

We shouldn’t reference thsis if we don’t need to do it. For instance, we shouldn’t reference it in an arrow function when it’s used as a callback.

We should write something like:

const cubed = arr.map(a => a ** 3);

instead of:

const cubed = arr.map(a => a ** 3, this);

or:

const containsE = array.some((char) => char === 'e', this);

Avoid Reverse

We should avoid using reverse if we aren’t using it to reversing an array.

If we’re just trying to combine results from the end of the array to the left, we can use reduceRight instead.

For example, instead of writing:

const sum = array.reverse().reduce((total, c) => total + c, 0);

We should write:

const reverseSum = array.reduceRight((total, c) => total + c, 0);

We skipped the reverse operation and did the same thing.

This means we write less code and the code is faster because we skipped one operation.

Use Flat Map

The latest version of JavaScript has a flatMap method to map array items and reduce the nesting of the array by 1 level. This is better than using map and flat together.

For example, instead of writing:

const flattenedAndMapped = array.map((a) => a).flat();

We can write:

const oneAction = array.flatMap((b) => b);

If we don’t need to map, we can also use flat :

const flattened = array.flat();

Use Flat

To flatten an array, we don’t need to use reduce or concat anymore. This is because array instances now have the flat method to reduce nesting.

For instance, instead of writing:

const concatFlattened = [].concat(...array);

or:

const reduceFlattened = array.reduce((p, n) => p.concat(n), []);

We instead write:

const flattened = array.flat();

No Unused Parameters

We shouldn’t have unused parameters in our arrow functions.

Since they aren’t used, we should remove them.

For instance, we write:

const fn = (data, user) => request(user.id, data);

instead of writing:

const fn = (data, user) => request(user.id);

data wasn’t used in the example above, so we should take it out.

Number of Parameters

We shouldn’t have too many parameters. The more parameters we have, the harder for us to keep track of them.

We may pass arguments in the wrong place and it’s also easy to pass in the wrong type of data.

Ideally, we have 3 parameters or less in our functions.

For example, good functions are:

const fn0 = () => "";

or:

const fn3 = (one, two, three) => one * two * three;

But the following isn’t good:

const fn3 = (a, b, c, d) => a * b * c * d;

If we need more than that, then we can pass in an object or use the rest operator.

For example, we can write:

const fn = ({ a, b, c, d }) => a * b * c * d;

We used the destructuring syntax to destructure the properties in an object.

Then we can pass in as many properties as we want in an object.

Also, we can use the rest operator as follows:

const fn = (a, b, c, ...d) => a * b * c * d[0];

d would be a parameter that has arguments in the 4th position and beyond store in an array.

Naming Functions

If we need to use arrow functions in multiple places, then we should name them by assigning them to a variable.

For instance, we can write:

cosnt fn = (a, b) => a ** b;

Now we can call it by writing fn(1, 2) .

We can also put them into an object:

const obj = {
  fn: (a, b) => a ** b;
}

Then we can call it by writing obj.fn(1, 2) ;

Conclusion

There are many methods in array instances that we can use to manipulate them.

Also, there are better ways to define arrow functions and use them.

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.