views:

346

answers:

5

I've noticed in javascript that if you define a function, say myfunction(), and then call myfunction.toString() you get the text of the source for that function. Are there any interesting/real world uses of this?

+4  A: 

I think this is essentially used for debugging purposes...

romaintaz
Can you give some examples of how you might use it for debugging purposes?
Matthew Lock
It happened to me one time: A call to one of my Javascript function didn't do what I was expecting. Using this toString() trick helped me to understand that the function I called was not the one I wrote. Indeed, this function was defined twice (one time by me, another time in a third-party .js file), and of course, my function was not the one who was called...But I must say that the call of the function.toString() is quite unusual...
romaintaz
+2  A: 

One real-world example is in Prototype, which uses this to determine the names of the arguments to methods in subclasses to see if it should do its special superclass handling stuff (which involves the special argument $super).

T.J. Crowder
So basically, it can be used for reflection?
Kobi
Basically, yeah, or even modification as shown by the accepted answer.
T.J. Crowder
A: 

apart from the usefulness of getting the content of a function as a string, or ensuring that the type of whatever you're working is a string (for non coercive comparisons), check out the optional radix parameter that can be used.

radix Optional. Specifies a radix for converting numeric values to strings. This value is only used for numbers.

var a = 16;
a.toString(2)    // returns "10000"
a.toString(4)    // returns "100"
a.toString(8)    // returns "20"
a.toString(16)   // returns "10"
a.toString(32)   // returns "g"

The following example illustrates the use of the toString method with a radix argument. The return value of function shown below is a Radix conversion table.

function CreateRadixTable (){
   var s, s1, s2, s3, x;                    //Declare variables.
   s = "Hex    Dec   Bin \n";               //Create table heading.
   for (x = 0; x < 16; x++)                 //Establish size of table
   {                                        // in terms of number of
      switch(x)                             // values shown.
      {                                     //Set intercolumn spacing.
         case 0 : 
            s1 = "      ";
            s2 = "    ";
            s3 = "   ";
            break;
         case 1 :
            s1 = "      ";
            s2 = "    ";
            s3 = "   ";
            break;
         case 2 :
            s3 = "  ";
            break;
         case 3 : 
            s3 = "  ";
            break;
         case 4 : 
            s3 = " ";
            break;
         case 5 :
            s3 = " ";
            break;
         case 6 : 
            s3 = " ";
            break;
         case 7 : 
            s3 = " ";
            break;
         case 8 :
            s3 = "" ;
            break;
         case 9 :
            s3 = "";
            break;
         default: 
            s1 = "     ";
            s2 = "";
            s3 = "    ";
      }                                     //Convert to hex, decimal & binary.
      s += " " + x.toString(16) + s1 + x.toString(10)
      s +=  s2 + s3 + x.toString(2)+ "\n";

   }
   return(s);                               //Return entire radix table.
}
Joshua
The question is about the Function.prototype.toString() method. Your reply is about the Number.prototype.toString() method.
NickFitz
no-one else pointed out the optional radix parameter. I thought it might be useful.
Joshua
+4  A: 

Well, you can use it to easily redefine a function:

function x() { alert('asdf'); }
eval(x.toString().replace('asdf','hello'));
x();

This will alert the string "hello" instead of the string "asdf".

This may be useful. On the other hand, self modifying code is often frowned upon because of the difficulty to maintain it...

Guffa
+1 for showing `eval` as the complement of `toString`. It doesn't have to be self modifying - you can change or copy code from different sources (when there isn't a better interface).
Kobi
This isn't guaranteed to work though. For example, certain browsers will always show strings within a function wrapped in double-quotes, even if they were single-quoted in the source. This means that any attempt to evaluate the result of fn.toString() will cause a syntax error if one of those strings contains a double-quote. I'm pretty sure this is a spec violation by those browsers, but it still means that this technique can't be used in the real world :-(
NickFitz
note that "private" variables of self invoked functions are still safe from this type of hooliganism. ie var x = function () { var a = 'asdf'; return {doit:function(){alert(a)}}}(); < there is no way to change "a" in this case, only the "public" returned "doit" function.
Joshua
+1  A: 

I've used it to automatically generate named-parameter versions of functions. For example, if you have a function

function f(a, b, c) {
    return a * b + c;
}

you can extract the parameter names from f.toString() and use it to generate a function that you can call like this:

namedParametersF({ a: 2, b: 3, c: 4}); // f(2, 3, 4);

Here's an implementation of this idea:

// Get an array of parameter names from a function
Function.parameters = function(f) {
    // Find the parameter list in f.toString()
    var m = /function[^\(]*\(([^\)]*)\)/.exec(f.toString());
    if (!m) {
        throw new TypeError("Invalid function in parameters");
    }

    var params = m[1].split(',');
    for (var i = 0; i < params.length; i++) {
        // trim possible spaces
        params[i] = params[i].replace(/^\s*|\s*$/g, '');
    }
    return params;
};

// Convert f to a function that accepts named parameters
Function.withNamedParameters = function(f) {
    var params = Function.parameters(f);
    return function(args) {
        var argsArray = new Array(params.length);
        for (var i = 0; i < params.length; i++) {
            argsArray[i] = args[params[i]];
        }
        return f.apply(this, argsArray);
    };
};

I have a slightly more flexible implementation of this that can go the other direction (Function.withPositionalParameters) on GitHub: http://gist.github.com/132782.

Matthew Crumley