javascript get function body
I have a function e.g.
var test = function () {alert(1);}
How can I get the body of this function?
I assume that the only way is to parse the result of test.toString() method, but开发者_StackOverflow is there any other way? If parsing is the only way, what will be the regex to get to body? (help with the regex is extremly needed, because I am not familiar with them)
IF(!!!) you can get the toString()
, then you can simply take the substring from the first indexOf("{")
to the lastIndexOf("}")
. So, something like this "works" (as seen on ideone.com):
var test = function () {alert(1);}
var entire = test.toString(); // this part may fail!
var body = entire.substring(entire.indexOf("{") + 1, entire.lastIndexOf("}"));
print(body); // "alert(1);"
2015 update
Upon revisiting the state of function decompilation, it can said that it's generally safe in certain well-considered use cases and enviroments (e.g: Node.js workers with user defined functions).
It should be put in the same bucket as eval, which is a powerful tool that has its place, but should only be used on rare occasions. Think twice, that's my only advice.
The conclusions from Kangax's new research:
- It's still not standard
- User-defined functions are generally looking sane
- There are oddball engines (especially when it comes to source code placement, whitespaces, comments, dead code)
- There might be future oddball engines (particularly mobile or unusual devices with conservative memory/power consumption)
- Bound functions don't show their original source (but do preserve identifier... sometimes)
- You could run into non-standard extensions (like Mozilla's expression closures)
- ES6 is coming, and functions can now look very different than they used to
- Minifiers/preprocessors are not your friend
"function decompilation" — a process of getting string representation of a Function object.
Function decompilation is generally recommended against, as it is a non-standard part of the language, and as a result, leads to code being non-interoperable and potentially error-prone.
@kangax on comp.lang.javascript
Simplest Use-Case
If you just want to execute the body of the function (e.g. with eval
or using the Worker
API), you can simply add some code to circumvent all the pitfalls of extracting the body of the function (which, as mentioned by others, is a bad idea in general):
'(' + myFunction + ')()';
I am using this trick in this Worker
-related JSFiddle.
Complete Function Serialization With Accurate Stacktrace
I also wrote a more complete library that can:
- Serialize any kind of function to string
- Be able to send that string representation anywhere else, execute it with any custom arguments, and be able to reproduce the original stacktrace
Check out my CodeBuilder
code here.
Note that much of the code takes care of making sure that we get an accurate stacktrace, wherever we execute the serialized function at a later point in time.
This fiddle demonstrates a simplified version of that logic:
- Use
JSON.stringify
to properly serialize the function (that comes in handy when, e.g., we want to make it part of a bigger serialization "data package"). - We then wrap it in one
eval
to un-escape the "JSON-ish"-escaped string (JSON does not allow functions + code, so we must useeval
), and then in anothereval
to get back the object we wanted. - We also use
//# sourceMappingURL
(or the old version//@ sourceMappingURL
) to show the right function name in the stacktrace. - You will find that the Stacktrace looks Ok, but it does not give you the correct row and column information relative to the file that we defined the serialized functions in, which is why my
Codebuilder
makes use of stacktracejs to fix that.
I use the CodeBuilder
stuff in my (now slightly dated) RPC library where you can find some examples of how it is used:
serializeInlineFunction
exampleserializeFunction
examplebuildFunctionCall
example
extending @polygenelubricants' answer:
using: .toString()
Testee:
var y = /* olo{lo} */
/* {alala} */function/* {ff} */ x/*{s}ls{
}ls*/(/*{*{*/)/* {ha-ha-ha} */
/*
it's a function
*/
{
return 'x';
// }
}
/*
*/
By indexOf
and lastIndexOf
:
function getFunctionBody(fn) {
function removeCommentsFromSource(str) {
return str.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '$1');
}
var s = removeCommentsFromSource( fn.toString() );
return s.substring(s.indexOf('{')+1, s.lastIndexOf('}'));
};
getFunctionBody(y);
/*
"
return 'x'
"
*/
used: rm comments from js source
var fn1 = function() {};
var fn2 = function() { alert("lol!"); };
Function.prototype.empty = function() {
var x = this.toString().match(/\s*function\s*\w*\s*\(.*?\)\s*{\s*}\s*;?\s*/);
return x != null;
};
alert(fn1.empty()); // true
alert(fn2.empty()); // false
' Solução proposta pelo Paulo Torres no grupo A.P.D.A. no facebook.
This code provides the body when using ES6 arrow functions like var testFn=(q)=>q+1;
function getFunctionBody(test){
var entire = test.toString(); // note: not safe-guarded; this part may fail like this!
return entire.substring((entire.indexOf("{")+1)||(entire.indexOf("=>")+2), entire.lastIndexOf("}")!==-1?entire.lastIndexOf("}"):entire.length);
}
//testing/showcase code
var tests = [
function () {alert(1);},
()=>{return 1;},
q=>q+1
];
for (var i=0;i<tests.length;i++){
console.log(tests[i],getFunctionBody(tests[i]));
}
I originally submitted this code as an edit to polygenelubricants accepted answer, but it was rejected as the changes were considered to be too drastic.
you can try this functiion:
function extractFunctionBody(fn) {
var reg = /function \((.*)\)[ ]?{(.*)}$/g;
var match = reg.exec(fn.toString().replace(/\n/g, ";"));
if (match){
return match[2];
} else {
return "";
}
}
Try this:
/\{(\s*?.*?)*?\}/g.exec(test.toString())[0]
test.toString() will hold your entire declaration.
/{(\s*?.?)?}/g will match everything between your braces
精彩评论