It depends on where and how the function is (or isn't) declared.
If it's a global and not declared via let name = ...
or const name = ...
syntax (and it's not a class constructor declared with class
), you can check by looking for it as a property on the global object. (Those caveats are all ES2015 things; more below.) You can get a reference to the global object via this
in loose mode at global scope; browsers also give you a global called window
. So assuming a browser:
if (typeof window[func_name] === "function") {
// ....
}
If it might not be a global, but rather is just in scope because your code closes over it, or if it was created using one of those ES2015 mechanisms I mentioned, there's really no good way to check other than eval
:
if (eval("typeof " + func_name) === "function") {
// ....
}
Using eval
is a last resort, and you must only use it with strictly-controlled input. But when you have to, and you have strictly-controlled input, it's fine.
About the ES2015 caveats:
The new let
, const
, and class
are very interesting beasties: When used at global scope, they create globals, but they don't create properties on the global object. As of ES2015, although all properties of the global object are globals, not all globals are properties of the global object. It's all part of trying to rein in vastly-polluted global namespace and also bring greater security to the JavaScript binding model. (Now that we have true modules.)
So (note that this will only run in cutting-edge browsers):
// Global scope, in a browser (because I used `window` and `document.body`) that
// implements this aspect of ES2015 (as I write this, Firefox's SpiderMonkey
// doesn't, Chrome's V8 does on the latest Chrome; expect SpiderMonkey and IE
// to catch up pretty quick (didn't test IE Edge, maybe it's already there)
// Strict mode isn't required for this behavior, but for the moment V8 only
// supports the block-scoped constructs in strict mode.
"use strict";
let tbody = setup();
// Old-fashioned var: Creates a property on the global object, so
// we get "function, function"
var f1 = function() { /*...*/ };
result("var declaration", typeof f1, typeof window["f1"]);
// Function declaration: Creates a property on the global object, so
// "function, function"
function f2() {}
result("function declaration", typeof f2, typeof window["f2"]);
// `let` declaration: Doesn't create property on global object, so
// "function, undefined"
let f3 = function() { /*...*/ };
result("let declaration", typeof f3, typeof window["f3"]);
// `const` declaration: Doesn't create property on global object, so
// "function, undefined"
const f4 = function() { /*...*/ };
result("const declaration", typeof f4, typeof window["f4"]);
// `class` declaration: Doesn't create property on global object, so
// "function, undefined"
class C1 {}
result("class declaration", typeof C1, typeof window["C1"]);
function setup() {
document.body.insertAdjacentHTML(
"beforeend",
"<table>" +
"<thead>" +
"<tr><th>test</th><th>global</th><th>prop</th></tr>" +
"</thead>" +
"<tbody></tbody>" +
"</table>"
);
return document.body.querySelector("tbody");
}
function result(label, direct, win) {
tbody.insertAdjacentHTML(
"beforeend",
"<tr><td>" + [label, direct, win].join("</td><td>") + "</td></tr>"
);
}
body {
font-family: sans-serif;
}
table {
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 4px 8px;
}
Output on cutting-edge browsers:
+----------------------+------------+-----------+
| test | global | prop |
+----------------------+------------+-----------+
| var declaration | function | function |
| function declaration | function | function |
| let declaration | function | undefined |
| const declaration | function | undefined |
| class declaration | function | undefined |
+----------------------+------------+-----------+
Note: Some transpilers don't enforce this rigorously, so if you see different results in transpiled code, don't be surprised.