3

I want to check if a function is defined (I don't care how, I mean it is callable)

sample code:

var functions = {
    'alert':'alert',
    'undefinedFunction':'undefinedFunction',
    'ff.showAlert':'ff.showAlert'
};

var ff = {
    showAlert: function() {
        alert('the function works');
    }
};

for (i in functions) {
    console.log(i+' : '+typeof window[functions [i]]);
}

this returns:

alert : function
undefinedFunction : undefined
ff.showAlert : undefined

console.log(typeof window.ff.showAlert); return function

Live demo
Is there a way to programmatically check if a function exists?

2
  • possible duplicate of testing if javascript function exists Commented May 4, 2012 at 13:02
  • no it is not, in that case he knows the function name, in my case I don't.. Commented May 4, 2012 at 13:17

5 Answers 5

5

The code:

window[functions [i]]

Is checking for window['ff.showAlert'] but what you really want to check for is:

window['ff']['showAlert']

or

window.ff.showAlert

For this, you need to traverse the namespace (window->ff->...):

function methodIsDefined(fn, obj) {
  var ns = fn.split('.');
  fn = ns.pop();
  do {
    if (!ns[0]) {
      return typeof obj[fn] === 'function';
    }
  } while(obj = obj[ns.shift()]);
  return false;
}

E.g.

methodIsDefined('ff.showAlert', window); // => true
methodIsDefined('ff.foo', window); // => false
Sign up to request clarification or add additional context in comments.

Comments

2

Your problem lies within the namespacing. The string "ff.showAlert" does not reference to the function window['ff']['showAlert'], but to window['ff.showAlert'], which is an important difference. The function you declare is actually referenced by window['ff']['showAlert']. So the place, that you are checking for its existance, is wrong.

If you want to check for the existance of such namespaced functions, you first have to split the string and then walk through the DOM to find the correct property. The code would be something like the following (not tested!):

function checkFunction( name ) {

  var path = "ff.showAlert".split( '.' ),
      runner = window;

  for( var i=0; i<path.length; i++ ) {
    if( path[i] in runner ) {
      runner = runner[ path[i] ];
    } else {
      return false;
    }
  }
  return runner;
}

edit

As pointed out by @VirtualBlackFox in the comments: The above solution only works if the function is declared below the window-scope. If it's inside another function's scope, you would have to pass that scope as an additional parameter and search there.

Even in that case you can't check for the existance of functions that are, e.g., defined inside some closure constructs.

4 Comments

If the code is executed in any function scope, it won't be under window as it is declared with var.
@VirtualBlackFox I just wanted to point him in the right direction and not provide a full blown solution for every case here. In a function scope, he would have to pass the actual scope as an additional parameter, which runner could start from. Of course even then some closure cases are not taken care of.
@skafandri Of course it does. Your function is declared as window['ff']['showAlert']. I just wanted to point out, that the string in the identifier is not parsed with respect to ..
@Sirko Yes i was just pointing a detail, I think that anyway he should force it's code to work using window. The only other working soution (eval) is ugly.
0

You need to split multi-part function names like 'ff.showAlert'. Also, because you specified ff as a var, it won't be a member of window unless it's outside of any function scope. It's not really clear whether it is or not from your code example.

Anyway, the function below allows you to pass in a base object, in case you need to specify one other than window, and it splits multi-part function names:

function isFnDefined(fnName, baseObj) {
    try {
        var parts = fnName.split('.'),
            ii;

        // If no baseObj was provided, default to 'window'.
        baseObj = baseObj || window;

        for (ii in parts) {
            baseObj = base[parts[ii]];
        }

        return typeof baseObj === 'function';
    }
    catch (e) {
        return false;
    }
}​

isFnDefined('alert');          // returns true
isFnDefined('undefinedFunc');  // returns false
isFnDefined('ff.showAlert');   // returns true, if ff is a member of window 
isFnDefined('showAlert', ff);  // returns true

Comments

-1

you need to use eval(): http://www.w3schools.com/jsref/jsref_eval.asp

1 Comment

No. Eval is not necessary here.
-2

You have a string that represent something in the current scope and you want to know what it is, the solution is to eval the string.

var f = undefined;
try
{
    f = eval(functions[i]);
}
catch(ReferenceError) {}
console.log(typeof f);

But why are you storing strings instead of function themselves in your input object ?

Also if you could force every string to be a reference relative to window (No 'var' in the current scope or something coming from the closure of another scope) then @Sirko solution might be the best one.

1 Comment

I have many buttons on the page, having several onclick actions, what I want to do is to disable buttons that try to call undefined functions..

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.