Categories
JavaScript Best Practices

JavaScript Antipatterns —Loops

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we write JavaScript loops.

for Loops

for loops are handy for iterating over arrays and array-like objects.

For instance, we can write:

const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
  // do something with arr[i]
}

This loops the indexes of the arr array and let us do something with each entry.

It also works with array-like objects. Nodelists are array-like objects.

We can call the following methods to get a Nodelist with multiple DOM elements:

  • document.querySelectorAll()
  • document.getElementsByName()
  • document.getElementsByClassName()
  • document.getElementsByTagName()

document.querySelectorAll() is the most versatile since it accepts any CSS selector.

document.getElementsByName only returns the items with the given name attribute.

document.getElementsByClassName only returns the items with the given class name.

document.getElementsByTagName returns items with the given tag name.

Also, the following properties of the document have specific items

  • document.images — all img elements on a page
  • document.links — all a elements
  • document.forms — all forms
  • document.forms[0].elements — all fields in a form

We can loop through them as follows:

for (let i = 0; i < document.links.length; i++) {
  // do something with document.links[i]
}

With the for loop, we can define all the initial conditions in the first expression, so we can write:

for (let i = 0, max = arr.length; i < max; i++) {
  // do something with arr[i]
}

which is the same as:

for (let i = 0; i < arr.length; i++) {
  // do something with arr[i]
}

i++ is the same as i = i + 1 or i += 1 .

i++ can be tricky if we assign it to something.

i++ returns the original value of i instead of the updated value.

So if we have:

let i = 1;
const a = i++;

We get that a is 1.

for-in Loops

for-in loops are useful for looping through keys of an object and its prototypes.

Therefore, if we only want to loop through the object’s non-inherited keys, we’ve to use the hasOwnProperty method.

For instance, we can use it by writing:

const obj = {
  a: 1,
  b: 2
}

for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    //...
  }
}

That will only loop through the keys that are non-inherited as hasOwnProperty checks that.

We can also call hasOwnProperty as follows:

const obj = {
  a: 1,
  b: 2
}

for (const key in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, key)) {
    //...
  }
}

This will avoid issues when the obj object redefined hasOwnProperty to be something else.

We can also write:

const obj = {
  a: 1,
  b: 2
}

const hasOwn = Object.prototype.hasOwnProperty;
for (const key in obj) {
  if (hasOwn.call(obj, key)) {
    //...
  }
}

to cache the hasOwnProperty method.

for-of Loop

The for-of loop is more versatile than the other loops. It’s useful for looping through the arrays and array-like objects.

So it’s a great alternative to the for loop for looping through those objects.

It also works with all the document properties listed above and also new data structures like sets and maps.

We can use it as follows:

for (const link of document.links) {
  // do something with link
}

For arrays, we can write:

for (const a of arr) {
  // do something with a
}

It also works with the destructuring assignment syntax:

for (const { foo, bar } of arr) {
  // do something with foo and bar
}

It also works great with sets and maps:

for (const s of set) {
  // do something with a
}

For maps, we can write:

const map = new Map([
  ['a', 1],
  ['b', 2]
])

for (const [key, value] of map) {
  // do something with key and value
}

Conclusion

A for loop is great for looping through arrays and array-like objects.

However, the for-loop beats that by letting us loop through any iterable object.

The for-in loop has limited uses for looping through keys in an object. If we want to loop through their prototype’s keys as well, then we can use that.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Arrays, JSON, and Regex

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we’re defining and using arrays, JSON, and regex.

Array Literals

We can define an array with the Array constructor with array literals.

The constructor has 2 versions. The one argument version is different from the one with multiple arguments.

If we pass in one argument, it’ll create an array with the given length with all empty entries.

If we pass in more than one argument, then an array with the arguments will be created.

For instance, we can create one as follows:

const empty = new Array(5);

or:

const arr = new Array(1, 2, 3);

Array Literal Syntax

The array literal syntax lets us create an array without the array constructor.

For instance, we can write:

const arr = [1, 2, 3];

This syntax is simple and straightforward.

It’s cleaner than using the new operator and the constructor.

Avoid the Array Constructor

Because of the different versions of the array constructor, then we would have issues because of the confusion between them.

If we pass in a floating-point number into the Array constructor, then we get an error.

This is because a floating-point number isn’t a valid length.

For instance, if we have:

const a = new Array(3.14);

Then we get ‘Uncaught RangeError: Invalid array length’.

Check if an object is an Array

Using the typeof operator isn’t useful for checking if an object is an array.

If we write:

console.log(typeof [1, 2]);

we get 'object' on the console log output.

Therefore, we can’t use the typeof operator to check if an object is an array.

We can use the code instanceof Array to check if something is an array.

It may fail in IE when used across frames, but this shouldn’t be an issue most of the time.

So we can use:

[1, 2] instanceof Array

Also, we can use the Array.isArray method, which does work across frames.

We can use it by writing:

Array.isArray(`[1, 2])`

It works across frames and only returns true if it’s actually an array.

JSON

JSON stands for JavaScript Object Notation.

It’s the main data transfer format for JavaScript apps.

It’s just the combination of arrays and the object literal notation.

The only difference between a JavaScript object and a JSON string is that we’ve to wrap keys with double-quotes.

JSON strings can’t have functions or regex literals.

Working with JSON

The best way to work with JSON is to use the JSON.parse method, which is available since ES5.

We can sue it to parse JSON strings to JavaScript objects.

To convert an object to a string, we can use the JSON.stringify method.

For instance, we can use them as follows:

const str = JSON.stringify({
  foo: 1,
  bar: new Date(),
})

Then we get:

"{"foo":1,"bar":"2020-04-20T15:18:51.339Z"}"

as the value of str .

To parse a JSON string, we can write:

const obj = JSON.parse("{\"foo\":1,\"bar\":\"2020-04-20T15:18:51.339Z\"}")

Once an object is converted to a string, then it’ll be converted back to a string.

So we get:

{foo: 1, bar: "2020-04-20T15:18:51.339Z"}

as the value of obj .

Regular Expression Literal

A regex in JavaScript is also an object.

We can create them using the RegExp constructor or a regex literal.

For instance, we can create it as follows:

const re = /foo/gm;

as a regex literal, or:

const re = new RegExp("foo", "gm");

using the constructor.

The regex literal is shorter and we don’t have to think about using the constructor.

So it’s better to use the literal.

If we use the constructor, we’ve to escape quotes and backslashes.

Therefore, it’s hard to read and modify.

To make things simple, we should stick with the literal notation.

Regular Expression Literal Syntax

The regex syntax uses forward slash to delimit them and then we have the modifiers.

They include:

  • g — global match
  • m — multiline
  • i — case insensitive match

The modifiers can be added in any order.

For instance, we can use regex literals in the string replace method:

const str = "abc123".replace(/[a-z]/gi, "");

Then we stripped all the letters our of the string and get 123 .

The regex matches all letters in the string and replaced them with empty strings.

Conclusion

We should use literals to define arrays and regex whenever we can.

Ther literal notations are usually shorter and cleaner.

Also, the Array.isArray method is great for checking if an object is an array.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Globals and Variables

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we write JavaScript code, including the avoidance of global variables.

Writing Maintainable Code

We got to write maintainable code to save us time when changing code.

Messy code is impossible to read and work with.

Even if we can do something with them, we’ll create bugs easily.

Minimizing Globals

We got to minimize the use of global variables.

This means that we shouldn’t create any global variables ourselves and we just use the ones that are built into the browser.

To avoid creating global variables, we should have strict mode on.

Strict mode is on for modules by default. And we can use the 'use strict' directive to turn it on for scripts.

We can put that anywhere in scripts, but we should put that at the top so that it applies to the whole script.

With strict mode on, we can’t write things like:

x = 1;

to create the global variable x .

However, even with a strict mode on, we can still attach properties to the window property to create global variables.

To avoid that, we just don’t write things like:

window.x = 1;

We just don’t want to deal with them as they can be changed anywhere so it’s hard to trace.

Also, their names can easily conflict.

The Problem with Globals

Global variables are shared by all scripts.

They live in the global namespace and are used not only by our own scripts but also 3rd party libraries, ads scripts, analytics scripts, etc.

To avoid creating global variables, we should use the let or const keywords.

Variables and constants created with them are block-scoped so that they’re only available within a block.

So we can write:

let x = 1;  
const y = 2;

and we won’t create global variables.

We also don’t want to write things like:

let x = y = 1;

since y would be a global variable while x is block-scoped.

The assignment is always evaluated right to left.

Side Effects When Forgetting let or const

If we forgot let or const , we’ll create global variables.

Access to the Global Object

The global object can be accessed with the window object in the browser.

However, we can create a function to access the global object in any context by writing:

const global = (function () {  return this; }());

At the top level, this is the global object.

So when we run that, global will be assigned to the window object in the browser.

This is because we didn’t invoke the function with the new keyword. Rather, we just called it directly.

Single let or const Pattern

let or const can be used to declare multiple variables and constants as follows:

let x = 1,  
  y = 2,  
  z = 3;

or we can write:

const x = 1,  
  y = 2,  
  z = 3;

They’ll all be let or const , so it’s different from chain assignments that we saw before.

It’s also great for assigning a property from the previous item to another variable.

For instance, we can write:

const el = document.querySelector("body"),  
  style = el.style;

We have the body DOM object assigned to el and then el.style is assigned to style all in one line.

Hoisting

We only have to worry about hosting when we use var to declare variables.

Hoisting is where the variable is available before it’s assigned.

Only the variable is available, but its value isn’t.

That’s just confusing, so it’s one reason that we shouldn’t use var .

Conclusion

There’re a few ways to write maintainable JavaScript code.

We should avoid global variables as much as possible.

Also, we should use let or const to declare variables and constants respectively.

If we really need global variables, we can access it safely by using a function.

We can also a property of a variable or constant that’s been assigned to the variable after if we use a comma to write multiple assignments.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Naming and Documentation

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we format JavaScript code, including naming and documentation.

Naming Conventions

JavaScript code has some naming conventions that are standard to it.

We should follow it so that we can have consistency within a project.

However, we can tweak them slightly to fit our own style as long as it doesn’t conflict with other people’s styles.

Capitalizing Constructors and Classes

Constructors are names PascalCase.

Each word has the first letter capitalized.

For instance, we can define one as follows:

function FooConstructor() {...}

or:

class MyClass {...}

Other functions are camelCase.

Separating Words

Most identifiers are usually written in camelCase except for constructors as we mentioned above.

For instance, we can define variables by writing let firstName; .

A function can be written as calculateArea() .

Constants are written in all upper case with words separated by an underscore.

For instance, we can write const MAX_DONUTS = 1;

Property names are also usually written in camelCase, so we get person.firstName = 'bob';

Other Naming Patterns

There’re some names that don’t follow those conventions.

For instance, there are constant properties in the Number object that are written in the upper case.

For instance, Number.MAX_SAFE_INTEGER is a constant that has an upper case property name.

That indicates that it’s constant.

We may also have properties beginning with an underscore to indicate that it’s private.

Since there are no private variables in constructors and classes, we may have to do that to indicate that we shouldn’t access it directly.

For instance, we may write:

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

We can do the same with methods. For instance, we can write:

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

  _greet() {
    //...
  }
}

Likewise, we can do the same with objects:

const person = {
  getName() {
    return `${this._getFirst()} ${this._getLast()}`;
  },
  _getFirst() {
    // ...
  },
  _getLast() {
    // ...
  }
};

_getFirst and _getLast are private as indicate by the underscore at the beginning of the name.

So we shouldn’t call them directly even though we can.

Alternatively, we can use the following conventions for private and protected members:

  • trailing underscore means private
  • leading underscore for protected and 2 for private

We can adapt this to our liking.

Writing Comments

We should write comments for code that isn’t immediately obvious.

That means we should write comments on every variable or statement.

We should just explain things that aren’t clear in the code.

Also, we can discuss any decisions that we made if we need to justify them.

Writing API Docs

API docs are essential. If we let external users use our API then we need to document them.

Even though it might be boring and unrewarding, we got to do it so that everyone knows how to use our APIs.

We can usually write some comments and convert them into API documentation by using things like JSDoc.

Writing to Be Read

We got to write our docs so that it’s actually useful to the readers who read it.

Otherwise, we’re wasting our time. And the readers of our documentation would be frustrated.

The information should be accurate and the structure got to be clear and easy to follow.

We probably have to go through multiple drafts to get them right.

Writing API docs also provides an opportunity to revisit our code, so we can take a look at the code and revision it at that time if needed.

We should assume that other people would read it. Otherwise, we wouldn’t have to write it in the first place.

Peer Reviews

Peer reviews also improve our code. Reviewers can provide suggestions for improvements.

It also lets us see other people’s styles and learn what we missed from there code.

In the end, reviews help us write clear code at least because we know someone else will read it.

Source control systems are also essential. Not only it helps us undo bad changes easily.

It also lets people look at our code when we check them in.

Conclusion

We should stick with some naming conventions. Like camelCase for variables and upper case for constants.

Writing docs is also important since we need to tell me how to use our stuff.

Peer reviews are also good since we can learn from other people.

Categories
JavaScript Best Practices

JavaScript Antipatterns — Immediate Functions

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

In this article, we’ll look at some antipatterns that we should avoid when we’re defining and using functions.

Returning Functions

Returning functions is possible in JavaScript since functions are just regular objects.

For instance, we can define a function that returns another function as follows:

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

We return a function that logs 'bar' in the function above.

We can call it if we call foo to return the function. Then we can call it:

const bar = foo();
bar();

Self-Defining Functions

If we assign a function to a variable that already holds a function, then the old function will overwrite the existing one.

For instance, we can write:

let foo = () => {
  console.log("foo");
  foo = () => {
    console.log("bar");
  };
};

Then if we call it twice, we’ll get 2 different results:

foo()
foo()

And we’ll get:

foo
bar

in the console log output.

However, it’s probably not very useful in most cases even though we can do it.

Immediate Functions

We can also define immediately invoked function expressions (IIFEs), which are functions that are created immediately and called.

For instance, we can write an IIFE as follows:

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

In the code above, we have the anonymous function that’s wrapped in parentheses.

Then we can call it right away by putting parentheses at the end.

This way, we don’t have to create a named function and call it somewhere else.

The pattern for creating an IIFE is as follows:

  • Define a function using a function expression or arrow function
  • Add a set of parentheses at the end to make the function run immediately.
  • Wrap the whole function in parentheses

This is useful because we can have variables that aren’t accessible to the outside.

It also can act as a module in that it can return things inside the function.

Then we can assign the returned values to a variable.

However, now that we have modules, we can use those instead of using IIFEs to hide private variables.

One good use of IIFEs is that we can use them to call async functions immediately after it’s defined.

So we can write:

(async () => {
  const val1 = await promise1;
  //...
  const val2 = await promise2;
  //...
})();

This way, we can use async functions without defining a named function.

Parameters of an Immediate Function

Immediate functions can also take parameters like any other function.

For instance, we can add parameters and use them as follows:

((firstName, lastName) => {
  console.log(`${firstName} ${lastName}`);
})('joe', 'smith');

The arguments are passed in at the end of the IIFE and run the function with those arguments right away.

So we get ‘joe smith’ from the console log output.

We can also use IIFEs to access global objects in a safe manner.

It lets us access global variables by using this at the top level.

So we can write:

const global = ((global) => {
  return global
})(this);

We pass in this from the outside, so we can use an arrow function to return the global parameter.

In the browser, this should be the window object.

Returned Values from Immediate Functions

We can return values from IIFEs as we saw before.

For instance, we can write the following:

const result = (() => {
  return 1 + 2;
})();

We’ll see 3 assigned to result since we called the function and returned the value right away.

It’s also useful for storing private data while returning others.

For instance, we can write:

const result = (() => {
  let x = 1;
  return x + 2;
})();

Then we get the same result. x isn’t available on the outside of the function.

IIFEs can also be used to define object properties.

So we can write something like:

const obj = {
  message: (() => {
    const who = "me",
      what = "call";
    return `${what} ${who}`;
  }()),

  getMsg() {
    return this.message;
  }
};

Then we get the message is 'call me' since the value of the template, the string is returned by the IIFE.

getMsg is the same because this is obj and message is 'call me' .

Conclusion

We can use IIFEs to store private data. Also, we can use them to return what we want.

They’re also handy for running async functions without defining a named function.

They take parameters like any other function.

Functions can also return other functions.