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?

ilyes kooli
  • 11,959
  • 14
  • 50
  • 79
  • possible duplicate of [testing if javascript function exists](http://stackoverflow.com/questions/798340/testing-if-javascript-function-exists) – Phil H May 04 '12 at 13:02
  • no it is not, in that case he **knows** the function name, in my case I don't.. – ilyes kooli May 04 '12 at 13:17

5 Answers5

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
James
  • 109,676
  • 31
  • 162
  • 175
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.

Sirko
  • 72,589
  • 19
  • 149
  • 183
  • If the code is executed in any function scope, it won't be under window as it is declared with var. – Julien Roncaglia May 04 '12 at 13:02
  • @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. – Sirko May 04 '12 at 13:04
  • @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 May 04 '12 at 13:06
  • @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. – Julien Roncaglia May 04 '12 at 13:07
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
FishBasketGordo
  • 22,904
  • 4
  • 58
  • 91
-1

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

Yair Nevet
  • 12,725
  • 14
  • 66
  • 108
-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.

Community
  • 1
  • 1
Julien Roncaglia
  • 17,397
  • 4
  • 57
  • 75
  • 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.. – ilyes kooli May 04 '12 at 13:08