Function.prototype.toString()
Returns the exact source text of a function, or a native code stub for built-ins.
toString()- Added in JavaScript ECMAScript 1 (1997)
Return value
stringA string representing the source code of the function.
Errors
Throws a TypeError ("Function.prototype.toString called on incompatible object") if its this value is not a Function object.
Changelog
- PHP ECMAScript 2018The return value must now be the exact source code as declared, including all whitespace and comments; if the host does not have the source available it must return a native function string instead. Before this, engines were free to normalize or reformat the output.
toString() hands back the source text a function was defined with — keywords, parameter list, body, whitespace and comments included. It is invoked automatically whenever a function is coerced to a string, so `${fn}` and fn + "" produce the same result.
When to reach for it
- Detecting native code. Built-ins, bound functions and host functions return
function name() { [native code] }; matching that string is how libraries tell a genuine built-in from a polyfill or a monkey patch. - Debug output and error messages that need to show the offending predicate.
- Shipping a function to a Worker as a string, or reading a parameter list for a dependency-injection container.
What trips people up
The returned text is not a closure. Reconstructing it with new Function() or eval() gives a function that has lost every variable it used to capture, so any code depending on outer scope quietly breaks. And native function strings are a guaranteed syntax error if you try to evaluate them.
Parsing the text is fragile too. Minifiers rename parameters, transpilers rewrite bodies, and comments survive into the output — the old Angular-style trick of reading dependency names from the source is exactly why such code needs a build-time annotation instead.
Example
When to use it
- Check whether a method is the original built-in or has been monkey-patched
- Include the failing predicate's source in an assertion error message
- Send a self-contained function to a Web Worker or another realm as text
- Print a function definition in a REPL or debugging tool
Frequently asked questions
Why does toString() return "[native code]"?
Because the function is implemented by the engine or host rather than in JavaScript — every built-in, every bound function, and anything from a non-JS host falls in this category. There is no source text to return, and evaluating the stub is a guaranteed syntax error.
Can I recreate a function from its toString() output?
Only if the function is fully self-contained. The string carries the source but not the closure, so new Function("return " + fn)() produces a function compiled in global scope that cannot see any variable the original captured.
Is it safe to parse parameter names out of a function's source?
No, not in production. Minifiers rename parameters, transpilers rewrite bodies, and comments and default values complicate the text. Frameworks that once did this now require explicit annotations or a build step for exactly this reason.
More examples
Detecting a native implementation
Array.prototype.map reports native code while a user-written arrow reports its source, which is the whole basis of "has this built-in been patched?" checks. Calling toString through Function.prototype on a non-function throws a TypeError rather than returning a string.
const isNative = (fn) =>
/\{\s*\[native code\]\s*\}$/.test(Function.prototype.toString.call(fn));
console.log(isNative(Array.prototype.map), isNative((x) => x));
// toString() is called implicitly on string coercion
console.log(`fn is: ${function hello() {}}`);
try {
Function.prototype.toString.call("foo");
} catch (e) {
console.log(e.constructor.name);
}Round-tripping source, and losing the closure
The source text preserves defaults and rest syntax, so the rebuilt greet keeps its name and a length of 0. The last block is the trap: reader's source mentions captured, but the rebuilt copy is compiled in global scope where that binding does not exist, so calling it throws a ReferenceError.
function greet(name = "world", ...rest) {
return `hi ${name}`;
}
const src = greet.toString();
console.log(src.slice(0, src.indexOf(")") + 1));
const restored = new Function("return " + src)();
console.log(restored("there"), restored.name, restored.length);
const captured = 42;
const reader = () => captured;
const rebuilt = new Function("return " + reader.toString())();
try { rebuilt(); } catch (e) { console.log("rebuilt lost its closure:", e.constructor.name); }
Discussion