Explain Codes LogoExplain Codes Logo

How to tell if a JavaScript function is defined

javascript
callbacks
functions
robustness
Nikita BarsukovbyNikita Barsukov·Dec 26, 2024
TLDR

Invaluable in its brevity, the typeof check reveals if the identifier even has a chance to be invoked. Here's how it fares:

typeof myFunction === "function" ? /* Function exists */ : /* Function doesn't exist, so chill */;

Function existence alone doesn't mean it's callable

In the lively stage of JavaScript, callbacks are passed around and functions may be invoked conditionally. Before executing a function, ensure its occurrence and functional identity to dodge the popular error message "callback is not a function" - a notorious, code crashing party pooper.

if (typeof highlightCode === "function") { highlightCode(myBigCodeBlock); } else { console.log('highlightCode was defined, but someone swapped it with a cat video URL'); }

This surefire way of evaluation promises you are calling real functions, not phantom functions or cat video URLs, fortifying your code's robustness against unexpected shenanigans.

Why repeat when you can define? - Introducing utility functions

Seeing typeof mentioned every now and then can get tedious, and may add a grey strand to your maintainability beard. Worry not, utility functions to the rescue!

function isFunction(thingToCheck) { return typeof thingToCheck === "function"; } // Usage: if (isFunction(printThis)) { printThis("Hello, StackOverflow!"); }

By using the isFunction utility function, you not only dodge repeating code, but also increase clarity. Now, the function check is so transparent that even a myFunction.isFunction("transparency") would return true.

Unexplored territories and edge cases

Even functions are citizens here

JavaScript treats functions as first-class citizens. They have the privilege to be passed around like variables. Also, they can morph into non-function entities, and throw a wrench into your function machinery unexpectedly:

let supposedlyBusyFunction = 123; if (isFunction(supposedlyBusyFunction)) { supposedlyBusyFunction(); // This won't serve martinis. }

Hence, always check if your function is truly a function, and not on a vacation.

Arrow functions and their class(y) friends

Different scoping and this binding behaviors are introduced to the function society with the advent of arrow functions and class methods. Here's how to check if they are indeed functions:

// Arrow function...pointy! const partyArrowFunction = () => { /* Invites everyone to the party */ }; // Class method...classy! class Party { startParty() { /* Starts the music */ } } let allNightParty = new Party(); console.log(isFunction(partyArrowFunction)); // true, party!!! console.log(isFunction(allNightParty.startParty)); // true, all night party!!!

Home is where the function is - or is it?

If you're performing checks in separate execution contexts like window or global, your function might be playing hide-and-seek due to scope:

// In a browser: if (isFunction(window.openTab)) { // openTab exists in the global scope of browser. } // In Node.js: if (isFunction(global.internetIsDown)) { // internetIsDown exists in the global scope of Node.js. }

Grasping the scope of your functions can help you chase them down, in case they try to hide in the multiverse of modules, contexts, and patterns.