Categories
JavaScript Best Practices

JavaScript Antipatterns — Functions

Spread the love

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.

Names and Hoisting

Function declarations ae hoisted. This means that they’re pulled up to the top.

Their definition can’t be assigned to variables or properties or appear in invocations as parameters.

For instance, if they’re at the top-level:

function foo() {}

Then they’re available everywhere.

It the function is inside a function as follows:

function local() {
  // local scope
  function bar() {}
  return bar;
}

Then bar is returned when we called local , so that it’s not available everywhere.

Function’s name Property

To get the name of the function, we can use the name property of the function.

For instance, if we write:

function foo() {}

Then we can check the name as follows:

console.log(foo.name);

However, if we have the following:

let bar = function foo() {}

Then bar.name is still 'foo' .

This also works with arrow functions. So if we write:

let bar = () => {}

Then bar.name is still 'bar' .

Function Hoisting

Functions declarations are hoisted.

So we can define a function declaration anywhere and call it.

For instance, we can write:

foo();

function foo() {
  console.log('foo');
}

And 'foo' will be logged.

However, if we the following function expression:

bar();

var bar = function() {
  alert('bar');
};

Then we get bar is not a function error.

This is one reason we shouldn’t use var to declare variables.

var only hoists the variable, but not its value.

This is a major source of confusion for many people looking at JavaScript apps.

Callback Pattern

Functions are objects, so we can pass them into other functions as parameters.

For instance, we can write the following:

function foo(callback) {
  // ...
  callback();
  // ...
}

or:

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

callback is a function that can be called.

A callback can be a standalone function or a method of an object.

For instance, we can write:

const person = {
  name: 'joe',
  getName() {
    console.log(this.name);
  }
};

const foo = (callback) => {
  callback();
}

foo(person.getName);

We don’t get 'joe' logged.

Instead, we should write:

const person = {
  name: 'joe',
  getName() {
    console.log(this.name);
  }
};

const foo = (callback, obj) => {
  callback.call(obj);
}

foo(person.getName, person);

We need the call method to call the callback with the object that we want to set as the value of this inside the callback function.

call lets us change the value of this and call it with arguments that we want.

We may also want to check if the value passed in is actually a function.

To do that, we can use the typeof operator to do that.

For instance, we can write:

const foo = (callback, obj) => {
  if (typeof callback === "function") {
    callback.call(obj);
  }
}

This will make sure that callback is actually a function before calling it.

Asynchronous Event Listeners

Async event listeners are used everywhere in JavaScript.

Therefore, we should be aware of them.

The callbacks passed in there are called in an indeterminate amount of time. For instance, when an event is triggered.

For instance, we can write:

document.addEventListener("click", console.log, false);

to log the clicks events triggered on the page.

We can replace 'click' with other events like 'keypress' , 'mouseover' , 'mousemove' , and many others.

Timeouts

Async callbacks are also used by timers.

So setTimeout and setInterval all take callbacks which let us call them in when the given amount of time in milliseconds has elapsed.

For instance, we can write:

setTimeout(() => console.log('time is up'), 500);

And we get that string logged in 500ms.

For setInterval , we can do the same thing:

setInterval(() => console.log('time is up'), 500);

Then the string will be logged every half a second.

Conclusion

Functions are first-class in JavaScript, which means that they’re treated like any object.

They have properties and can be passed into other functions as arguments.

This is why we can have callbacks in JavaScript. We can pass in a function and call it inside another function.

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 *