There are two methods to call a function from string stored in a variable. The first one is by using the window object method and the second one is by using eval() method. The eval() method is older and it is deprecated.
Define a function named "myFunction", and make it display "Hello World!" in the <p> element. Hint: Use the function keyword to define the function (followed by a name, followed by parentheses). Place the code you want executed by the function, inside curly brackets. Then, call the function.
To convert a function to string, use the toString() method of a function object.
toString . For user-defined Function objects, the toString method returns a string containing the source text segment which was used to define the function. JavaScript calls the toString method automatically when a Function is to be represented as a text value, e.g. when a function is concatenated with a string.
if you know that its a global function you can use:
var functPtr = window[func_name];
//functPtr()
Otherwise replace window with the parent object containing the function.
I just did a quick test in Firebug and I was able to get the function from the name by simply eval()
ing the name... I feel dirty using eval()
, but it seems to get the job done here quite nicely.
var myFunctionPtr = eval(func_name);
This is never a preferred approach. Instead of keeping the function name in func_name
, you could have kept the reference to a function just as successfully in something like func_to_call
.
If you absolutely require to keep the function reference as a string, you would typically use a hash table to map an arbitrary name to a variable (JS has first-class functions which makes it possible)
myFunction = function(){};
var obj = {func_name: myFunction};
obj['func_name']();//executes the function
Fiddled (I don't know why, it's a such a tiny script :)
It was suggested that you use eval(func_name)
- however this could quickly get out of control because of JS scoping.
You have also declared your myFunction = function(){};
as a global variable. On one hand, it lets you reference it as window[func_name]
but on the other hand it pollutes the global scope.
this[func_name] should give you the function.
var myfunc = this[func_name];
myfunc();
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.
A safe way to do is to sandbox the alleged function while testing its type:
function isFunction(expr) {
function sandboxTemplate() {
var window, document, alert; // etc.
try {
return typeof $expr$ == "function";
} catch (e) {
return false;
}
}
try {
var sandbox = new Function(
sandboxTemplate.toString().replace("$expr$", expr)
+ "return sandboxTemplate()");
return sandbox();
} catch (e) {
return false;
}
}
function test(expr) {
document.write("<div>\"" + expr + "\" <b>is "
+ (isFunction(expr) ? "" : "not ")
+ "</b>a function</div>");
}
/* Let's do some testing */
function realFunction() {
}
test("realFunction"); // exists!
test("notHere"); // non-existent
test("alert('Malicious')"); // attempt to execute malicious code!
test("syntax error {"); // attempt to blow us up!
The output:
The sandboxing code could be written in a more concise manner but I like using "template" functions instead of embedding JS code as string literals.
And oh, this does it nicely without using eval
-- though one can argue that using a Function constructor is no different than an eval
.
Use eval:
myFunction = function(){};
var func_name = "myFunction";
var myFunctionPtr = eval(func_name);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With