Last updated: Mar 3, 2024
Reading timeยท4 min
Use the typeof
operator to check if a function is defined.
The typeof
operator returns a string that indicates the type of a value. If
the function is not defined, the typeof
operator returns "undefined"
and
doesn't throw an error.
function greet() { return 'hello world'; } if (typeof greet === 'function') { // ๐๏ธ this runs console.log('โ function is defined'); console.log(greet()); // ๐๏ธ hello world } else { console.log('โ๏ธ function is NOT defined'); }
We used the typeof operator to check if a function is defined.
The operator returns a string that indicates the type of a value. Here are some examples:
console.log(typeof (() => {})); // ๐๏ธ "function" console.log(typeof function () {}); // ๐๏ธ "function" console.log(typeof null); // ๐๏ธ "object" console.log(typeof []); // ๐๏ธ "object" console.log(typeof {}); // ๐๏ธ "object" console.log(typeof ''); // ๐๏ธ "string" console.log(typeof 0); // ๐๏ธ "number"
The typeof
operator doesn't throw an error when used with a variable that is
not declared, instead, it returns the string "undefined"
.
console.log(typeof doesNotExist); // ๐๏ธ "undefined"
However, if you use the typeof
operator before declaring a variable with the
let
or const
keywords, you would get an error.
// โ๏ธ ReferenceError: Cannot access 'myFunction' before initialization if (typeof myFunction === 'function') { console.log('โ function is defined'); } else { console.log('โ๏ธ function is NOT defined'); } const myFunction = () => {};
We used the typeof
operator before initializing a function using the const
keyword.
We would get the same result if we declared the function using the let
keyword.
However, if we use the var
keyword, we won't get an error because of how
hoisting works in
JavaScript.
if (typeof myFunction === 'function') { console.log('โ function is defined'); } else { // ๐๏ธ This runs console.log('โ๏ธ function is NOT defined'); } var myFunction = () => {};
We declared the function using the var
keyword, so we didn't get an error when
using the typeof
operator, however, the else
block ran.
This is what happens under the hood when using the var
keyword.
var myFunction; if (typeof myFunction === 'function') { console.log('โ function is defined'); } else { // ๐๏ธ This runs console.log('โ๏ธ function is NOT defined'); } myFunction = () => {};
The declaration of the variable gets hoisted to the top, however, the assignment of the value remains where it is.
Named functions get hoisted to the top, so you can call a named function even before the line on which it is declared.
if (typeof greet === 'function') { // ๐๏ธ this runs console.log('โ function is defined'); console.log(greet()); // ๐๏ธ hello world } else { console.log('โ๏ธ function is NOT defined'); } function greet() { return 'hello world'; }
You can imagine that the declaration of the greet()
function was moved to
line 1.
When the if
statement runs, the function is available in the scope and can be
called.
Note that only named functions (ones that use the function
keyword) get
hoisted to the top of the file.
try/catch
blockThis is a three-step process:
try/catch
statement.try
block will run successfully.catch
block will run.try { const result = greet(); console.log(result); // ๐๏ธ hello world } catch (err) { console.log('The function is not defined'); } function greet() { return 'hello world'; }
We called the function in a try/catch
block.
The function is defined and available in the scope, so the try
block
completed.
If the function is not defined, the catch
block runs.
try { const result = greet(); console.log(result); } catch (err) { // ๐๏ธ this runs console.log('The function is not defined'); }
We tried to call a function that doesn't exist in the scope, so the error got
passed to the catch
block.
The catch
block handles the error, so your program won't crash due to an
unhandled error.
instanceof
You can also use the instanceof
operator to check if a variable is a function.
The instanceof
operator will return true
if the variable is of type function
and false
otherwise.
function sum(a, b) { return a + b; } if (sum instanceof Function) { // ๐๏ธ this runs console.log('โ The variable is of type function'); } else { console.log('โ๏ธ The variable is NOT of type function'); }
The syntax for the
instanceof operator
is object instanceof constructor
.
The operator returns true
if the prototype
property of the constructor
appears anywhere in the prototype chain of the object.
instanceof
operator is that it won't work as expected if you're checking a function that is from another document, e.g. an iframe.Each document will have its own Function
object, so the instanceof
check
should not be used in this scenario.
The typeof
operator would work as expected even if the function comes from a
different document (e.g. an iframe).
You can also convert a value to a string to check if it is a function.
function isTypeFunction(f) { const functionTypes = [ '[object Function]', '[object AsyncFunction]', ]; return functionTypes.includes( Object.prototype.toString.call(f), ); } // โ synchronous function function subtract(a, b) { return a - b; } // โ asynchronous function async function sum(a, b) { return a + b; } console.log(isTypeFunction(sum)); // ๐๏ธ true console.log(isTypeFunction(subtract)); // ๐๏ธ true console.log(isTypeFunction('abc')); // ๐๏ธ false
We used the toString()
method to convert the passed-in value to a string and
compared the output to the result of calling the method with a function.
// โ for sync functions function subtract(a, b) { return a - b; } // ๐๏ธ "[object Function]" console.log(Object.prototype.toString.call(subtract)); // -------------------------------------------------- // โ for async functions async function sum(a, b) { return a + b; } // ๐๏ธ "[object AsyncFunction]" console.log(Object.prototype.toString.call(sum));
Converting a synchronous function to a string returns "[object Function]", whereas converting an async function to a string returns "[object AsyncFunction]".
This code sample is for demonstration purposes only as there is no good reason to use this approach.
Using the typeof
operator should be your preferred approach as it is the most
performant, readable and works between different contexts (e.g. in iframes).