Verifique se uma variável é do tipo de função


904

Suponha que eu tenha qualquer variável, definida da seguinte maneira:

var a = function() {/* Statements */};

Eu quero uma função que verifica se o tipo da variável é semelhante a uma função. ou seja:

function foo(v) {if (v is function type?) {/* do something */}};
foo(a);

Como posso verificar se a variável aé do tipo Functionda maneira definida acima?


2
aqui uma referência para isso a maioria das formas comuns de verificar isso jsben.ch/#/e5Ogr
EscapeNetscape

Respostas:


380

O caminho do sublinhado é mais eficiente, mas a melhor maneira de verificar, quando a eficiência não é um problema, está escrita na página do sublinhado vinculada por @Paul Rosania.

Inspirada no sublinhado, a função final isFunction é a seguinte:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}

1
Pode haver alguém que tenha feito uma pesquisa mais extensa sobre o assunto, mas dê uma olhada nos resultados da revisão 4 do jsperf com uma simples "verificação de resultados" . isFunctionAmostra uma diferença de implementação em comparação com os outros métodos.
Joel Purra

24
Com os testes de desempenho atualizados , parece que há uma enorme diferença de velocidade, dependendo do seu navegador. No Chrome, typeof(obj) === 'function'parece ser o mais rápido de longe; no entanto, no Firefox obj instanceof Functioné o vencedor claro.
Justin Warkentin

7
Em relação à parte: typeof deve ser usada apenas para verificar se variáveis ​​ou propriedades estão indefinidas. No javascript.info/tutorial/type-detection na Um bom uso de typeof seção e na seguinte o autor afirma, que o caso é exatamente o oposto
Konrad Madej

11
Alex, estou curioso por que você diz que typeof só deve ser usado para verificar se não está definido. Seu nome sugere que seu objetivo é verificar o tipo de algo, não apenas se ele existe. Existem problemas ou problemas técnicos que podem surgir ao usá-lo para fazer a verificação de tipo ou é mais uma preferência? Se houver advertências, seria bom listá-las para que outras pessoas evitem tropeçar nelas.
precisa saber é o seguinte

14
injustificadamente solução complicada
Daniel Garmoshka

1713
if (typeof v === "function") {
    // do something
}

44
Apenas atente para algumas coisas como typeof Object, typeof Datee typeof Stringque todo o retorno 'function'também.
Dave Ward

358
@ Dave, não tenho certeza qual é o problema, uma vez que essas são funções.
Matthew Crumley

6
Qual é a diferença if (v instanceOf Function)?
mquandalle

10
@mquandalle Não há grande diferença, exceto instanceofque não funcionará se você estiver verificando uma função que veio de outro documento (um iframe, talvez). O desempenho varia.
rvighne

8
diferente da resposta aceita, isso também funciona para funções assíncronas. Para um asyncfunctionToCheck, getType.toString.call(functionToCheck)==='[object AsyncFunction]'onde comotypeof asyncfunctionToCheck === 'function'
TDreama

132

O Underscore.js usa um teste mais elaborado, mas de alto desempenho:

_.isFunction = function(obj) {
  return !!(obj && obj.constructor && obj.call && obj.apply);
};

Consulte: http://jsperf.com/alternative-isfunction-implementations

EDIT: testes atualizados sugerem que o typeof pode ser mais rápido, consulte http://jsperf.com/alternative-isfunction-implementations/4


Existe alguma razão específica para usar essa abordagem typof?
sv_in 14/05

1
A versão do Underscore é muito mais rápida na maioria dos navegadores. Veja: jsperf.com/alternative-isfunction-implementations
Paul Rosania

13
Eu com certeza gosto de sublinhado, simples, mas poderoso. Dito isso, provavelmente vale a pena notar que essa implementação pode ser falsificada por um objeto com esses atributos.
precisa saber é o seguinte

3
A @PaulRosania encontrou esses testes de desempenho hoje cedo e os atualizou com verificação e mais dados de teste como revisão 4 - execute-o para aumentar a cobertura dos testes do navegador. É mais lento nas operações por segundo (devido aos muitos testes), mas também mostra uma diferença de implementação (abra o console javascript e recarregue a página, pode haver mensagens de erro registradas). Nota: A revisão 3 adicionou o isFunctionD (baseado apenas typeof == "function") - e parece ser muito mais rápido que a versão "rápida" do Underscore.
Joel Purra

6
Esta resposta é um pouco enganadora agora, já que o Underscore.js está na revisão 12 , e não na revisão 4 acima mencionada, de seu teste de isFunction()implementações alternativas . Atualmente, usa algo muito próximo ao sugerido pelo dandeano .
Jonathan Eunice

112

Existem várias maneiras, então vou resumir todas elas

  1. A melhor maneira é:
    function foo (v) {if (v instanceof Function) {/ * faça algo * /}};
    
    
    Solução com melhor desempenho (sem comparação de cadeias) e elegante - a instância do operador é suportada nos navegadores há muito tempo, portanto, não se preocupe - funcionará no IE 6.
  2. A melhor maneira seguinte é:
    function foo (v) {if (typeof v === "function") {/ * faça algo * /}};
    
    
    A desvantagem typeofé que ele é suscetível a falhas silenciosas, ruim; portanto, se você tiver um erro de digitação (por exemplo, "fusion") - nesse caso, o `if` retornará falso e você não saberá que há um erro até mais tarde. seu código
  3. A próxima melhor maneira é:
    função isFunction (functionToCheck) {
        var getType = {};
        retornar functionToCheck && getType.toString.call (functionToCheck) === '[função do objeto]';
    }
    
    
    Isso não tem vantagem sobre a solução 1 ou 2, mas é muito menos legível. Uma versão melhorada disso é
    função isFunction (x) {
        retornar Object.prototype.toString.call (x) == '[função do objeto]';
    }
    
    
    mas ainda muito menos semântica que a solução nº 1

10
A primeira solução falha no caso de uma função passada para um contexto de quadro diferente. Por exemplo, de um iframe top.Function !== Function. Para ter certeza, use o segundo (qualquer erro ortográfico de "função" seria corrigido durante a depuração, espero).
maxart

Em relação ao seu ponto de vista sobre erros de digitação: qualquer código que tenha esse erro de digitação provavelmente falhará rapidamente / não é mais aparente do que quaisquer outros erros baseados em erros de digitação. typeof === "function" é a solução mais limpa. Além disso, sua “melhor” solução em que você usa instanceofnão leva em consideração várias globais, como uma verificação entre quadros, e pode retornar falsos negativos.
brainkim


56

@grandecomplex: Há uma quantidade razoável de verbosidade na sua solução. Seria muito mais claro se escrito assim:

function isFunction(x) {
  return Object.prototype.toString.call(x) == '[object Function]';
}

Object.prototype.toString.call é útil para outros tipos de javascript nos quais você está interessado. Você pode até comparar com nulos ou indefinidos, o que o torna muito poderoso.
21812 Jason Foglia

49

var foo = function(){};
if (typeof foo === "function") {
  alert("is function")
}


1
... e (() => (typeof obj === 'function') && doSomething())ainda é a opção mais rápida.
darcher

35

Experimente o instanceofoperador: parece que todas as funções herdam da Functionclasse:

// Test data
var f1 = function () { alert("test"); }
var o1 = { Name: "Object_1" };
F_est = function () { };
var o2 = new F_est();

// Results
alert(f1 instanceof Function); // true
alert(o1 instanceof Function); // false
alert(o2 instanceof Function); // false

19

Algo com mais suporte ao navegador e também inclui funções assíncronas pode ser:

const isFunction = value => value && (Object.prototype.toString.call(value) === "[object Function]" || "function" === typeof value || value instanceof Function);

e depois teste como:

isFunction(isFunction); //true
isFunction(function(){}); //true
isFunction(()=> {}); //true
isFunction(()=> {return 1}); //true
isFunction(async function asyncFunction(){}); //true
isFunction(Array); //true
isFunction(Date); //true
isFunction(Object); //true
isFunction(Number); //true
isFunction(String); //true
isFunction(Symbol); //true
isFunction({}); //false
isFunction([]); //false
isFunction("function"); //false
isFunction(true); //false
isFunction(1); //false
isFunction("Alireza Dezfoolian"); //false


9

Para quem está interessado em estilo funcional ou procura uma abordagem mais expressiva para utilizar em metaprogramação (como verificação de tipo), pode ser interessante ver a biblioteca Ramda para realizar essa tarefa.

O código seguinte contém apenas funções puras e sem ponto:

const R = require('ramda');

const isPrototypeEquals = R.pipe(Object.getPrototypeOf, R.equals);

const equalsSyncFunction = isPrototypeEquals(() => {});

const isSyncFunction = R.pipe(Object.getPrototypeOf, equalsSyncFunction);

No ES2017, as asyncfunções estão disponíveis, para que possamos verificar também:

const equalsAsyncFunction = isPrototypeEquals(async () => {});

const isAsyncFunction = R.pipe(Object.getPrototypeOf, equalsAsyncFunction);

E depois combine-os:

const isFunction = R.either(isSyncFunction, isAsyncFunction);

Obviamente, a função deve ser protegida contra nulle undefinedvalores, para torná-la "segura":

const safeIsFunction = R.unless(R.isNil, isFunction);

E, complete o snippet para resumir:

const R = require('ramda');

const isPrototypeEquals = R.pipe(Object.getPrototypeOf, R.equals);

const equalsSyncFunction = isPrototypeEquals(() => {});
const equalsAsyncFunction = isPrototypeEquals(async () => {});

const isSyncFunction = R.pipe(Object.getPrototypeOf, equalsSyncFunction);
const isAsyncFunction = R.pipe(Object.getPrototypeOf, equalsAsyncFunction);

const isFunction = R.either(isSyncFunction, isAsyncFunction);

const safeIsFunction = R.unless(R.isNil, isFunction);

// ---

console.log(safeIsFunction( function () {} ));
console.log(safeIsFunction( () => {} ));
console.log(safeIsFunction( (async () => {}) ));
console.log(safeIsFunction( new class {} ));
console.log(safeIsFunction( {} ));
console.log(safeIsFunction( [] ));
console.log(safeIsFunction( 'a' ));
console.log(safeIsFunction( 1 ));
console.log(safeIsFunction( null ));
console.log(safeIsFunction( undefined ));

No entanto, observe que esta solução pode mostrar menos desempenho do que outras opções disponíveis devido ao uso extensivo de funções de ordem superior.


7

Se você usa o Lodash, pode fazê-lo com _.isFunction .

_.isFunction(function(){});
// => true

_.isFunction(/abc/);
// => false

_.isFunction(true);
// => false

_.isFunction(null);
// => false

Este método retorna truese value for uma função, caso contrário false.


4

Descobri que ao testar funções do navegador nativo no IE8, usando toString, instanceofe typeofnão funcionou. Aqui está um método que funciona bem no IE8 (tanto quanto eu sei):

function isFn(f){
    return !!(f && f.call && f.apply);
}
//Returns true in IE7/8
isFn(document.getElementById);

Como alternativa, você pode verificar as funções nativas usando:

"getElementById" in document

No entanto, li em algum lugar que isso nem sempre funcionará no IE7 e abaixo.


4

O abaixo parece funcionar para mim também (testado a partir de node.js):

var isFunction = function(o) {
     return Function.prototype.isPrototypeOf(o);
};

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false

4

Eu acho que você pode apenas definir um sinalizador no protótipo Function e verificar se a instância que você deseja testar herdou esse

defina uma bandeira:

Function.prototype.isFunction = true; 

e depois verifique se existe

var foo = function(){};
foo.isFunction; // will return true

A desvantagem é que outro protótipo pode definir o mesmo sinalizador e, em seguida, é inútil, mas se você tiver controle total sobre os módulos incluídos, é a maneira mais fácil


Ele falhará com o erro se foo for indefinido. Sem mencionar que você modifica o protótipo nativo que está totalmente errado.
Kamil Orzechowski

3

Desde o nó v0.11, você pode usar a função util padrão:

var util = require('util');
util.isFunction('foo');

2
util.isFunction está obsoleto
kehers

2

você deve usar o typeOfoperador em js.

var a=function(){
    alert("fun a");
}
alert(typeof a);// alerts "function"

0

A solução, como algumas respostas anteriores mostraram, é usar typeof. a seguir está um trecho de código Nos NodeJs,

    function startx() {
      console.log("startx function called.");
    }

 var fct= {};
 fct["/startx"] = startx;

if (typeof fct[/startx] === 'function') { //check if function then execute it
    fct[/startx]();
  }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.