I am playing around with some office JavaScript and attempting to create an executable function from a string that is received from an API call.
The office JavaScript task pane for Excel makes a call to an external API on button click, and returns a function in the form of a String object. To make this into a function object, I have used:
var executable = new Function(response)
executable();
Unfortunately, nothing is happening, it doesn't seem to be calling the function at all.
After some debugging, I believe the reason it isn't getting called is because the response string object is already a full function, and new Function()
is wrapping the response in another layer of function.
Response is:
async function highlightCells() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sheet1");
const range = sheet.getRange();
range.format.fill.color = "yellow";
await context.sync();
console.log("Called");
});
}
And executable is resolving to:
function anonymous() {
async function highlightCells() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sheet1");
const range = sheet.getRange();
range.format.fill.color = "yellow";
await context.sync();
console.log("Called");
});
}
}
Any ideas how to prevent the additional function wrapper from appearing? As you can see the response object is already a full function.
Do I need to use some other method of converting the string to a function or is there a way to override the wrapper within the new Function()
syntax?
CodePudding user response:
If you don't know the function name in advance, you can wrap the function definition in brackets to call it.
let response = `async function test() {
console.log("function called");
}`;
let executable = new Function(`(${response})();`);
executable();
If you need to pass it arguments or await it, make it return the function and call the function to get your actual function.
let func = `async function sum(a,b) {
return new Promise(resolve => setTimeout(() => resolve(a b), 1000));
}`;
let executable = new Function(`return ${func};`)();
(async () => {
let val = await executable(3,4);
console.log("the sum is", val);
})();
CodePudding user response:
If you know that it is guaranteed to be a function you could directly invoke it in the Function
:
let data = 'function(arg1, arg2) { return arg1 " " arg2 }'
let func = new Function(`return (${data}).apply(this, arguments)`)
console.log(func(1,2))
With .apply(this, arguments)
you call that function and pass the arguments you pass to your Function
object into the received function. And the return
returns the result that function.
Passing this
to apply
ensures that the Function
object could be stored in an object and that the function your received could access that object using this
. This might not be required, but makes the function behave like a regular function:
let data = 'function(arg1, arg2) { return arg1 " " arg2 " " this.prop }'
let obj = {prop : 'somevalue'}
obj.func = new Function(`return (${data}).apply(this, arguments)`)
console.log(obj.func(1, 2))
CodePudding user response:
Simply use eval
instead of new Function
. You need to force the code to be an expression, not a statement, that's what the 0,
part is for.
code = `function test() {
console.log('hey!')
}`
let executable = eval('0,' code)
executable()
CodePudding user response:
var executable = new Function(response ';highlightCells()')
executable();
CodePudding user response:
Since the returned code is a complete executable statement, use eval()
to execute it, not new Function()
.
eval(response);
highlightCells();
Note that this requires that you know the name of the function that's being defined in the response. If not, you need to write code that parses it to extract the function name.