views:

147

answers:

3

I'm trying to write a helper method in JavaScript. It should act differently if one sends in a function or an reference to a function.

I want to like to use it like this:

helper('div', function () { return false; })
helper('div', obj.fn)

What I can't figure out is: how to inside the helper function tell the difference between the two?

I think it's due to that JavaScript first evaluates the obj.fn before it sends it in. The only workaround I found is to send the obj.fn as an obj, i.e.

helper('div', { fn: obj.fn })

Then I can tell the difference between the two with typeof. But I really like some way to make it without the extra object declaration.

+2  A: 

UPDATED *(AGAIN): I thought that the toString() method might be your only way forward here. It doesn't however treat a reference of an anonymous object differently.

This code demonstrates that:

function acceptparam(fn){

      console.log("fn.constructor = " + fn.constructor);
      console.log("typeof fn = " + typeof fn);
      console.log("fn toString " + fn.toString());

      console.log("fn.prototype = " + fn.prototype);
      console.log("fn.prototype.constructor = " + fn.prototype.constructor);
      console.log("this[0] = " + this[0]);
      console.log("---");

     }

     function empty(){
      return ;
     }

     var x = {
      y : function(){return;}
     }

     acceptparam(empty);
     acceptparam(function(){return;});
     acceptparam(x.y);

Very interesting question, without implementing your own solution I dont think you can do it, this post helps to explain why. Its about the parent child relationship only being one way.

http://codingforums.com/showthread.php?t=134855

Lewis
You were right about toString... look at the console output carefully: `fn toString function empty()` vs. `fn toString function ()`
Skilldrick
Which is what Andy E's answer is doing :P
Skilldrick
Thats not what he's asking for thoguh, which is where I went wrong originally! fredrik is asking for the difference between an anonymous function and an object with a child function, which doesnt work.
Lewis
I see. This is hurting my brain now.
Skilldrick
@Lewis, that's true to an extent but functions that are properties of an object can be declared as named functions. See my updated answer.
Andy E
+1 for well written code and a decent answer
Andy E
+1  A: 

You can use toString() to find out if the function is anonymous assuming it is declared as a named function and not an unnamed function assigned to a variable:

See updated code below for an improved regex for IE

function jim () { var h = "hello"; }
function jeff(func) 
{ 
    var fName;
    var inFunc = func.toString();
    var rExp   = /^function ([^\s]+) \(\)/;
    if (fName = inFunc.match(rExp))
       fName = fName[1];

    alert(fName);
}

Will give you the name of the function if any.

jeff(function () { blah(); }); // alert: null;
jeff(function joe () { blah(); }); // alert: "joe";
jeff(jack); // "jack" if jack is function jack () { }, null if jack = function() {}

EDIT Lewis clarified the question for me and his answer is correct to the extent that functions declared as object properties are unnamed, but this answer does state that only named functions work and you can still declare an object property via a named function:

var jim = { };
function jeff(func) 
{ 
    var fName;
    var inFunc = func.toString();
    var rExp   = /^function ([^\s(]+)\s?\(\)/;
    if (fName = inFunc.match(rExp))
       fName = fName[1];

    window.prompt(fName, inFunc);
}
(function () { function jim.jack () {} })();
jeff(jim.jack);

The downside to this is that named functions that are properties of objects must be within a child function of the current scope because named functions are set at parse time as opposed to unnamed functions which are set at execution time:

var jim = {};
function jim.jack () {} // error
(function () { function jim.jack () {} })(); // no error
Andy E
I thought `jack = function () {}` was the same as `function jack() {}`, or maybe I'm getting mixed up with Scheme...
Skilldrick
`function jack() {}` is a named function, `jack = function () {}` is an unnamed function assigned to a variable.
Andy E
I'd love to know what the -1 was for...
Andy E
it differs: http://stackoverflow.com/questions/336859/javascript-var-functionname-function-vs-function-functionname
gpilotino
Yes, I've just checked and you're right. As far as I'm concerned, this is the correct answer, as long as functions being passed in are named functions. +1
Skilldrick
@gpilotino: what do you mean?
Skilldrick
@Andy E - +1 for the nice through answer, I reckon thats about as good as it will get 'out of the box'
Lewis
@Lewis, thanks. I just can't understand why I keep getting voted down!
Andy E
Thanks all for an extremely fast and good answer. Just change the reqexp to rExp = /^function\s([^\s]+)\s?\(([a-zA-Z0-9,\s]+)?\)/;Since it didn't work properly with whitespaces and arguments.I'll post the my solution below.
fredrik
A: 

I thought I'd add another alternative answer, mainly because I didn't want to add to the soup that is my other answer but also because it didn't go down to well with the stackoverflow voters that don't leave constructive comments ;-)

As an alternative to what you're trying to do, you could add a third parameter to the helper function:

function helper (tagName, fn, method)
{
    if (method)
        fn = fn[method];

    //- Do rest of helper function here
}
//- Now if we pass an object method to helper function we can identify it properly
helper('div', obj, "fn"); // method is obj.fn
helper('div', function () { blah(); }); // Still works fine

Merely a suggestion and works as well as or even better than your current work-around.

Andy E
Thanks. But actually that's was my first thing I tried. But I wanted to make it as simple as possible. But been messing around with this for awhile now. And it looks like I have to specify the obj that holds the function due to I want then the fn is triggered it should be in the right scoop. So that the this-keyword is refered to obj.
fredrik