Como saber se uma função JavaScript está definida

302

Como você sabe se uma função em JavaScript está definida?

Eu quero fazer algo assim

function something_cool(text, callback) {
    alert(text);
    if( callback != null ) callback();
}

Mas isso me dá um

retorno de chamada não é uma função

erro quando o retorno de chamada não está definido.

Aaron Lee
fonte

Respostas:

475
typeof callback === "function"
Tom Ritter
fonte
47
Essa não é realmente uma string mágica, pois o conjunto de valores typeof é definido com precisão na especificação da ECMA. Embora a sintaxe para isso seja um pouco boba, é um Javascript idiomático perfeitamente razoável.
Ben Zotto
2
@ Quixoto - entendido, acho que o que quero dizer é que você ainda precisa ter uma string independentemente - prefiro não ter mais strings literais espalhando meu código do que o absolutamente necessário; portanto, este não é o meu ideal para testar se algo é uma função.
Jason Bunting
4
E sim, "mágica" foi uma má escolha de palavras desleixada - eu quis dizer "string literal" e não "string mágica". Foi mal. :) #
22610 Jason Bunting
1
JSPerf - jsperf.com/…
Chris GW Green
Remova os parênteses da sua função e use apenas o nome da função na condição if ou a função será invocada em vez de fornecer verdadeiro ou falso.
Russell Strauss
236

Todas as respostas atuais usam uma string literal, que eu prefiro não ter no meu código, se possível - isso não acontece (e fornece um significado semântico valioso para inicializar):

function isFunction(possibleFunction) {
  return typeof(possibleFunction) === typeof(Function);
}

Pessoalmente, tento reduzir o número de strings penduradas no meu código ...


Além disso, embora eu saiba que typeofé um operador e não uma função, há pouco dano ao usar a sintaxe que a faz aparecer como a última.

Jason Bunting
fonte
7
Como devo usar esta função? Eu tentei isFunction(not_defined)), onde not_definedé o nome da função que não está definido e FireBug retornou not_defined is not definedque é correto, mas a sua função deve retornar falso e não gerar erro ...
Wookie88
@ Wookie88: Tecnicamente falando, a função ilustrada na minha resposta não retorna um erro, mas ocorre porque o intérprete JavaScript está tentando resolver o símbolo not_definedpara passar seu valor isFunction()e não consegue resolvê-lo. Nada pode ser feito na definição de isFunction()para resolver este problema.
31812 Jason Bunting
3
@ZacharyYates A maneira de contornar o ReferenceError significaria evitar trabalhar com uma referência à função talvez não definida. Você pode fazer a verificação de tipo dentro da chamada de função e passar o resultado para a função. jsfiddle.net/qNaxJ Talvez não seja tão legal, mas pelo menos não use strings;)
netiul
8
Ou evite uma função isFunctione faça exatamente (typeof no_function == typeof Function).
netiul
2
@JasonBunting Se você for tão exigente, realmente deve estar usando typeof(Function())porque Function é uma função; mas o mesmo acontece com Array, Object e outros protótipos internos (por falta de um termo melhor). Se você estivesse verificando uma matriz, não usaria, typeof(array) === typeof(Array)por exemplo; você usaria typeof(array) === typeof(Array())porque realmente precisa avaliar a função se estiver sendo cuidadoso e consistente.
Seanmhanson 26/10/2015
16
if (callback && typeof(callback) == "function")

Note-se que retorno de chamada (por si só) avalia que falsese é undefined, null, 0, ou false. Comparando com nullé excessivamente específico.

Robin como o pássaro
fonte
2
Observe também que, se for callbackavaliado como um valor "falso-y", seu tipo de nunca poderá ser "função".
21412 Joe
5
@ Joe, mas devido a um curto-circuito, esse teste não será executado se o retorno de chamada for falso.
Fabio A.
3
esta solução não está funcionando, porque a primeira condição acionará uma exceção imediatamente. isso pode funcionar para uma propriedade de um objeto: if (obj.callback) {...}
Roey
8

Esses métodos para saber se uma função é implementada também falham se a variável não estiver definida, por isso estamos usando algo mais poderoso que suporta o recebimento de uma string:

function isFunctionDefined(functionName) {
    if(eval("typeof(" + functionName + ") == typeof(Function)")) {
        return true;
    }
}

if (isFunctionDefined('myFunction')) {
    myFunction(foo);
}
patriciorocca
fonte
Eu acho que é uma boa resposta, você está certo, todos os métodos acima falham é que a função não está definida.
Matteo Conta
É trivial evitar referências indefinidas - basta consultar em typeof window.doesthisexistvez de doesthisexist. No entanto, o uso de eval (que: é ruim), como mostrado, funcionará apenas com funções nomeadas - isso não funcionaria com o caso de uso da pergunta.
AD7six 06/11/19
Esteja ciente de que isso funciona apenas com escopo global, enquanto a resposta aceita também funciona com funções de escopo local.
Inf3rno 14/10/19
6

Novo no JavaScript Não tenho certeza se o comportamento mudou, mas a solução dada por Jason Bunting (6 anos atrás) não funcionará se a função FunctionFun não estiver definida.

function isFunction(possibleFunction) {
  return (typeof(possibleFunction) == typeof(Function));
}

Isso gerará um ReferenceError: possibleFunction is not definederro quando o mecanismo tentar resolver o símbolo possibleFunction (conforme mencionado nos comentários da resposta de Jason)

Para evitar esse comportamento, você só pode passar o nome da função que deseja verificar se ela existe. assim

var possibleFunction = possibleFunction || {};
if (!isFunction(possibleFunction)) return false;

Isso define uma variável para ser a função que você deseja verificar ou o objeto vazio, se não estiver definido, evitando assim os problemas mencionados acima.

NectarSoft
fonte
Para ser completamente claro: meu código não gera o erro, é apenas o analisador JavaScript fazendo isso. O mesmo aconteceria se você tentasse usar qualquer identificador indefinido dessa maneira.
Jason Bunting
6

Experimentar:

if (typeof(callback) == 'function')
bdukes
fonte
Olá, não use a comparação "loose" (==) sempre use '===' para comparar até o tipo da variável.
Joel Carneiro
4
function something_cool(text, callback){
    alert(text);
    if(typeof(callback)=='function'){ 
        callback(); 
    };
}
ConroyP
fonte
4
if ('function' === typeof callback) ...
Andrew Hedges
fonte
3
Um pouco pedante, e ainda usa uma string literal. Que tal if(typeof Function === typeof callback)...? :)
Jason Bunting
@JasonBunting Curioso, o que há de errado com o uso de string literal?
Bsienn
@ Bsienn - como eu disse, é pedante da minha parte, mas aqui está a coisa. Digamos que você use o código com a string literal e a chamada typeofretorne algo diferente mais tarde (talvez "Function" em vez de "function"), devido a uma nova / diferente implementação de JavaScript - parece que seria menos provável que uma implementação nova / diferente interromperia a typeof Function === typeof callbackverificação porque deveria ser a mesma no nível semântico.
23617 Jason Bunting
4
typeof(callback) == "function"
dashtinejad
fonte
4

eu devo fazer

try{
    callback();
}catch(e){};

Eu sei que há uma resposta aceita, mas ninguém sugeriu isso. Não tenho muita certeza se isso se encaixa na descrição do idiomático, mas funciona para todos os casos.

Nos mecanismos JavaScript mais recentes, finallypode ser usado um.

Quentin Engles
fonte
Este é um atalho puro! Embora não funcione apenas para testar se uma função existe ou está definida (sem também executá-la).
Beejor
Isso é verdade. Assumi uma situação em que o retorno de chamada é opcional nesse ponto de execução. Eu costumo usar typeof callbackassim mesmo.
Quentin Engles
Isso faz sentido; Eu estava focando mais no título da pergunta do que no seu conteúdo. Às vezes, gosto da simplicidade de usar o try-catch em toneladas de testes para alguma coisa. Você acha que há uma desvantagem técnica em usá-lo, nesse caso, contra algo como o typeof? Ou é principalmente uma questão de fazer as coisas de uma maneira mais adequada / esperada?
Beejor 12/09/2015
1
Se você deseja testar vários tipos, as exceções não são tão boas. Por exemplo, o projeto no qual estou trabalhando agora usa muita verificação de tipo em uma variável para ver se é uma função, string ou qualquer outra coisa. Neste projeto, eu realmente preciso dos tipos. Para isso, uso uma matriz de tipos e verifico accepted_types.indexOf(typeof value) !== -1se ela está em uma variedade de tipos. Nesta situação, as exceções seriam proibitivas em minha opinião.
Quentin Engles
2

Tente o seguinte:

callback instanceof Function
eWolf
fonte
1
Não funciona Ainda lhe dará o erro. Você deve experimentá-lo antes de publicá-lo como resposta.
vbullinger 6/09/12
@ vbullinger Acabei de testar novamente no Chrome, Firefox e Safari - funciona em qualquer lugar. Com qual mecanismo você teve problemas?
eWolf
Raposa de fogo. Você testou o caso quando o retorno de chamada não foi definido?
vbullinger
Isso ocorre porque você está referenciando diretamente uma variável indefinida, que nunca funciona: pastebin.com/UQz2AmzH
eWolf
3
Funciona quando usado em um parâmetro de função, que é o que o autor solicitou - suponho que o ECMAScript distinga os casos de uma variável inexistente e de uma variável existente com um valor indefinido. Seria interessante saber mais sobre isso.
eWolf
2

Se você olhar a fonte da biblioteca @Venkat Sudheer Reddy Aedama mencionada, underscorejs, poderá ver o seguinte:

_.isFunction = function(obj) {
  return typeof obj == 'function' || false;
};

Esta é apenas a minha DICA, DICA da resposta:>

VentyCZ
fonte
2

Experimentar:

if (!(typeof(callback)=='undefined')) {...}
Brian
fonte
1

Eu estava procurando como verificar se uma função jQuery foi definida e não a encontrei facilmente.

Talvez possa precisar;)

if(typeof jQuery.fn.datepicker !== "undefined")
miguelmpn
fonte
o mesmo que de type0f($.datepicker) !== undefiledoutra forma seráobject
vladkras
0

Se callback()você está chamando não apenas uma vez em uma função, você pode inicializar o argumento para reutilização:

callback = (typeof callback === "function") ? callback : function(){};

Por exemplo:

function something_cool(text, callback) {
    // Initialize arguments
    callback = (typeof callback === "function") ? callback : function(){};

    alert(text);

    if (text==='waitAnotherAJAX') {
        anotherAJAX(callback);
    } else {
        callback();
    }
}

A limitação é que ele sempre executará o argumento de retorno de chamada, embora seja indefinido.

Nick Tsai
fonte
0

Para funções globais, você pode usar este em vez de evalsugerido em uma das respostas.

var global = (function (){
    return this;
})();

if (typeof(global.f) != "function")
    global.f = function f1_shim (){
        // commonly used by polyfill libs
    };

Você pode usar global.f instanceof Functiontambém, mas afaik. o valor de Functionserá diferente em quadros diferentes; portanto, ele funcionará apenas com um aplicativo de quadro único corretamente. É por isso que costumamos usar typeof. Observe que em alguns ambientes também pode haver anomalias typeof f, por exemplo, no MSIE 6-8, algumas das funções, por exemplo, alerttinham o tipo "objeto".

Por funções locais, você pode usar o da resposta aceita. Você pode testar se a função é local ou global também.

if (typeof(f) == "function")
    if (global.f === f)
        console.log("f is a global function");
    else
        console.log("f is a local function");

Para responder à pergunta, o código de exemplo está funcionando para mim sem erros nos navegadores mais recentes, então não tenho certeza de qual era o problema:

function something_cool(text, callback) {
    alert(text);
    if( callback != null ) callback();
}

Nota: eu usaria em callback !== undefinedvez de callback != null, mas eles fazem quase o mesmo.

inf3rno
fonte
0

A maioria, senão todas as respostas anteriores, têm efeitos colaterais para invocar a função

aqui melhores práticas

você tem função

function myFunction() {
        var x=1;
    }
maneira direta de testar

//direct way
        if( (typeof window.myFunction)=='function')
            alert('myFunction is function')
        else
            alert('myFunction is not defined');
usando uma sequência para que você possa ter apenas um lugar para definir o nome da função

//byString
        var strFunctionName='myFunction'
        if( (typeof window[strFunctionName])=='function')
            alert(s+' is function');
        else
            alert(s+' is not defined');

TexWiller
fonte
0

Se você deseja redefinir funções, é melhor usar variáveis ​​de função, definidas em sua ordem de ocorrência, uma vez que as funções são definidas globalmente, independentemente de onde elas ocorram.

Exemplo de criação de uma nova função que chama uma função anterior com o mesmo nome:

A=function() {...} // first definition
...
if (typeof A==='function')
   oldA=A;
A=function() {...oldA()...} // new definition
David Spector
fonte
0

Isso funcionou para mim

if( cb && typeof( eval( cb ) ) === "function" ){
    eval( cb + "()" );
}
Patrick Ogbuitepu
fonte
-2

Solução de uma linha:

function something_cool(text, callback){
    callback && callback();
}
Samir Alajmovic
fonte
Seu exemplo mostra exatamente isso, que ele deseja que a função seja chamada se for definida, e se não for, nada acontece. jsfiddle.net/nh1cxxg6 As funções que retornam valores falsey / false ainda são chamadas. Obviamente, isso não funciona quando você deseja que valores sejam retornados.
Samir Alajmovic 18/01/16
Ah, eu interpretei mal a pergunta dele. No entanto, isso causaria uma exceção se o retorno de chamada não for uma função.
Frank Bryce