2

Is there a way to obtain function's name from outside of it?

Lets say there is a js script on web page that we cannot modificate, just read. The script contains object, which contains objects and functions. Lets say that we want to find function named "HelloWorld".

With firebug, we loop through these objects and methods with a script, which looks something like this

// Parameter is target object.
function getFunctionNames(obj) {

    // For each objects / functions
    for (var id in obj) {

        // Focus only on functions
        if (typeof(obj[id]) == "function") {

            // Get name of the function.
            // console.log("Function: " + obj[id].toString()); 

            // Code above returns a block of code without the name. Example output:
            // Function: function(name) { alert("Hello World! Hello " + name + "!"); }
            //
            // Expected output would be
            // Function: HelloWorld
        }
    }
}
  • obj[id].toString() returns a block of code instead of a name.
  • obj[id].name returns an empty string. Anonymous function(?).
  • I cannot use arguments.callee.name because I cannot modify the target code.

I could just browse objects and functions in firebug or just read source code, but I'm looking a way to do it with Javascript.

Edit

For real world example, head to Youtube and try to get the name of function "setMsg()" from "yt" object via Javascript.

Edit2

Accepting Simon's answer for being kinda closest what I was looking for. It appears that I was seeking variable name, rather than function name. While answer didn't help me on original problem, it surely answered to original question. Paul Draper's comments helped me to right direction.

Thanks!

Mraok
  • 65
  • 6
  • what are you trying to acheive? WHat do functions you are trying to find do? Perhaps there's other approaches. Real world example would help – charlietfl Nov 03 '13 at 00:49
  • `yt.setMsg` has no name; i.e. it is an anonymous function. Try `yt.setMsg.toString()`. – Paul Draper Nov 03 '13 at 02:14
  • @PaulDraper So what you are trying to say is that there are no ways to get function's name outside of it, other than obj.name or by parsing it from obj.toString()? – Mraok Nov 03 '13 at 02:22
  • "Outside" or "inside" makes no difference. But it depends what you mean by the function's name. What is the name of `yt.setMsg`? Do you want `'yt.setMsg'`. If so, note that the same function can have many names, for example `var a = function() { return 1; }; var b = a;`. – Paul Draper Nov 03 '13 at 02:27
  • @PaulDraper I was thinking that I would find the name `setMsg` or `yt.setMsg`, just like it appears in firebug console. Surely(?) it exists in some form, since firebug can find it from memory named like that. – Mraok Nov 03 '13 at 02:52
  • That is certainly possible, but remember you aren't getting the name of the function anymore; you are getting the name of the variables and/or properties which are set to that function. For example, in `{a: function something() { }}`, "a" is the property name and "something" is the function name. – Paul Draper Nov 03 '13 at 03:00

2 Answers2

2

Use obj.name

Note that arguments.callee returns a function. name is property on every function (though it's empty for anonymous functions), so that's why arguments.callee.name works.

This works for webkit (Chrome and Safari), Firefox, and possibly others. It does not work for IE: function.name not supported in IE.

Community
  • 1
  • 1
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
1

As mentioned, the function doesn't have any intrinsic name other than the "" it gets from being an anonymous function. Some browsers (Firefox, probably Chrome, maybe others) do however perform some limited form of static analysis to figure out names of declared functions, to help with error stack traces. You can get to it in an relatively cross-browser way by getting setMsg to throw an exception and then parse exc.stack:

// cheat with .% in Firebug; there might be other ways of doing this, I dunno:
yt.setMsg.%m.za.__defineSetter__('a', function() { throw new Error(); });

try { yt.setMsg('a', 'a'); }
catch(e) { alert(e.stack.split('\n')[2].split('@')[0]); }

... On the other hand, this is a pretty terrible hack and dependent on the actual function involved (and if you know the function, you probably know its name already). It does work a bit more reliably when done from inside the function.

If you restrict yourself to just Firefox and are doing this for debug purposes, there are better ways of getting to it. Set devtools.chrome.enabled to true in about:config, open a Scratchpad (Shift+F4), set it to environment: browser, and run the following:

Components.utils.import("resource://gre/modules/jsdebugger.jsm");
window.addDebuggerToGlobal(window);

dbg = new Debugger();
dw = dbg.addDebuggee(content);

f = content.wrappedJSObject.yt.setMsg;
name = dw.makeDebuggeeValue(f).displayName;

dbg.removeDebuggee(content);

alert(name);

In both cases, you will note that it alerts "m.ya" instead of "setMsg", and indeed this is because the function was originally declared as m.ya = function() { ...; }. There is no reason why "setMsg" would be a preferable name, from the point of the browser.

Simon Lindholm
  • 2,266
  • 1
  • 21
  • 12