Reputation: 704
I have a given function that takes, among other arguments, two optional arguments which may be functions. Both need to be optional, one will be a function, and one will either be a boolean or a function that returns a boolean.
// Obj.func(variable, String[, Object][, Boolean||Function][, Function]);
Obj.func = function(other, assorted, args, BoolOrFunc, SecondFunc) {
// execution
};
Obj.func(
[
'some',
'data'
],
'of varying types',
{
and: 'some optional arguments'
},
function() {
if(some condition) {
return true;
}
return false;
},
function() {
// do things without returning
}
);
It is my desire that both functions (and several of the other arguments, if it matters) be optional, which means I have code in the function to determine which arguments the user intended to use.
Unfortunately, as both may be functions, and may be specified directly in the function call, I cannot simply use typeof
or instanceof
conditionals. However, since the first function, if it exists, will always return a boolean (and the second function will not return at all), one idea I had would be to check its return value:
if(typeof BoolOrFunc === 'boolean'
|| (typeof BoolOrFunc === 'function' && typeof BoolOrFunc() === 'boolean')) {
// BoolOrFunc is either a boolean or a function that returns a boolean.
// Handle it as the intended argument.
} else {
// Otherwise, assume value passed as BoolOrFunc is actually SecondFunc,
// and BoolOrFunc is undefined.
}
This works in principle; however, running typeof BoolOrFunc()
executes the function, which causes a problem if the function does more than just return a boolean: that is, if the function passed as BoolOrFunc
is actually meant to be SecondFunc
. SecondFunc
, in this case, is something of a callback function, and may perform actions, including DOM modifications, that I don't want to execute immediately.
For this reason, my question is: Is there a way to check if a function returns without executing it?
One thing I had considered was to call BoolOrFunc.toString()
, then perform a Regular Expression search for the return value, something along the lines of…
if(typeof BoolOrFunc === 'boolean'
|| (typeof BoolOrFunc === 'function'
&& BoolOrFunc.toString().search(/return (true|false);/) !== -1)) {
// BoolOrFunc is either a boolean or contains a return string with a boolean.
// Handle it as the intended argument.
}
Note that the above code may not work as written: I've not actually built a test case for it, because, well, it seems exceptionally inefficient and potentially unreliable, and I figured someone here might have a more elegant solution to my quandary. That having been said, I figured I'd include it for discussion purposes.
Upvotes: 8
Views: 2495
Reputation: 17453
Interesting question, if I understand correctly. First, I'd probably warn against having such open-ended arguments. I'm curious as to the use case.
But that said, it's easy enough to get around the problems you mention (which may not wholly solve your problem).
This works in principle; however, running typeof BoolOrFunc() executes the function...
That's easy enough to fix, I think. At first, just check to see if BoolOrFunc
is a boolean or a function (just typeof BoolOrFunc
, natch). If it's a boolean, I assume we're fine. Either SecondFunc
is missing (undefined
) or a function
-- or we're in an error state. That's all easy to handle in this first case.
So let's assume the second case, that we have BoolOrFunc-As-Function
. We're going to have to execute it, whether it's "really" BoolOrFunc
or SecondFunc
, so, at the last moment possible in your code, do so.
This is obviously where things get complicated without more pseudo-code (or production code) from you. If the "possible BoolOrFunc
" returns true
or false
, you know you have a function version of BoolOrFunc
. If it returns undefined
, you know you just called SecondFunc
. Again, depending on what you're trying to do, you branch here. If you have a true
or false
, you have to process out "BoolOrFunc-as-function", else you've called SecondFunc
and are likely done.
That doesn't answer the more generic question of how to tell if a function returns a specific type without calling it, but does solve your use case as presented here.
But this specific more generic case [sic] isn't too difficult, as long as we're dealing with real, constant booleans in the return
s. In that case, the regexp route would work -- find all return\s+.*;
and make sure what follows each return
is true
or false
. Or just compare total return
count is the same as that for return\s+(false|true);
. Or something. And hope the javascript in the function is well formed. What a bear already.
If the function can return local variables (return MightBeBoolean;
rather than return true;
), you're nearly toast -- or if you accept truthiness or falsiness, you're nearly toast again, since undefined
is falsey, and you can't tell the difference between BoolOrFunc
or SecondFunc
(which would always return "falsey"). It's possible, but not nearly worth writing the parser in any reasonable use case I can imagine. And if you're importing a lib to do that, sheesh. Good luck. Seems like overkill to have function that's slightly more defensive in its calling.
I do wonder if either functions passed as arguments are themselves passed arguments from your Obj.func
, which would make things even more interesting.
Got more real code? ;^) But ultimately I'm recommending you don't do the "pass any jumble of arguments, as long as they're in order". I think you can likely do the BoolOrFunc/SecondFunc late evaluation. Otherwise, I hate to say it, but code a bit more offensively here.
Upvotes: 0
Reputation: 3279
Meshaal made a prediction in the question:
"... one will either be a boolean or a function that returns a boolean."
"... first function, if it exists, will always return a boolean."
With this prediction the function is not a Turing Machine, because we know that it returns something. Surely it can't be done with a simple RegExp: just return !0
would break the example.
But if you parse the result of function.toString() with a parser being intelligent enough to find all possible return points, the problem should principally be solvable.
Upvotes: 1