Vérifiez si une variable est de type fonction

904

Supposons que j'ai une variable, qui est définie comme suit:

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

Je veux une fonction qui vérifie si le type de la variable ressemble à une fonction. c'est à dire :

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

Comment puis-je vérifier si la variable aest de type Functionde la manière définie ci-dessus?

Jesufer Vn
la source
2
ici une référence pour ces moyens les plus courants de vérifier ce jsben.ch/#/e5Ogr
EscapeNetscape

Réponses:

380

Bien sûr, la méthode de soulignement est plus efficace, mais la meilleure façon de vérifier, lorsque l'efficacité n'est pas un problème, est écrite sur la page de soulignement liée par @Paul Rosania.

Inspirée par le soulignement, la fonction finale isFunction est la suivante:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
Alex Grande
la source
1
Il peut y avoir quelqu'un qui a fait des recherches plus approfondies sur la question, mais regardez les résultats de la révision 4 du jsperf avec une simple "vérification des résultats" . isFunctionAmontre une différence d'implémentation par rapport aux autres méthodes.
Joel Purra
24
Avec des tests de performances mis à jour, il semble qu'il y ait une énorme différence de vitesse en fonction de votre navigateur. Dans Chrome, il typeof(obj) === 'function'semble que ce soit de loin le plus rapide; cependant, dans Firefox obj instanceof Functionest le vainqueur clair.
Justin Warkentin
7
Concernant la partie: typeof ne doit être utilisé que pour vérifier si les variables ou les propriétés ne sont pas définies. À javascript.info/tutorial/type-detection dans la section Une bonne utilisation de typeof et la suivante, l'auteur déclare que le cas est exactement le contraire
Konrad Madej
11
Alex, je suis curieux de savoir pourquoi vous dites que typeof ne devrait être utilisé que pour vérifier s'il n'est pas défini. Son nom suggère que son but est de vérifier le type de quelque chose, pas seulement s'il existe. Y a-t-il des problèmes techniques ou des problèmes qui peuvent survenir lors de son utilisation pour effectuer une vérification de type, ou s'agit-il plutôt d'une préférence? S'il y a des mises en garde, il serait bon de les énumérer afin que d'autres puissent éviter de trébucher dessus.
jinglesthula
14
solution excessivement compliquée
Daniel Garmoshka
1714
if (typeof v === "function") {
    // do something
}
Selbie
la source
44
Il suffit de regarder quelques choses comme typeof Object, typeof Dateet typeof Stringqui tout retour 'function'aussi.
Dave Ward
359
@ Dave, je ne sais pas quel est le problème car ce sont des fonctions.
Matthew Crumley
6
Quelle est la différence avec if (v instanceOf Function)?
mquandalle
10
@mquandalle Aucune grande différence, sauf que instanceofcela ne fonctionnera pas si vous vérifiez une fonction provenant d'un autre document (un iframe, peut-être). Les performances varient.
rvighne
8
contrairement à la réponse acceptée, cela fonctionne aussi pour les fonctions asynchrones. Pour un asyncfunctionToCheck, getType.toString.call(functionToCheck)==='[object AsyncFunction]'où commetypeof asyncfunctionToCheck === 'function'
TDreama
132

Underscore.js utilise un test plus élaboré mais très performant:

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

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

EDIT: des tests mis à jour suggèrent que typeof pourrait être plus rapide, voir http://jsperf.com/alternative-isfunction-implementations/4

Paul Rosania
la source
Y a-t-il une raison particulière d'utiliser cette approche typof?
sv_in
1
La version Underscore est beaucoup plus rapide dans la plupart des navigateurs. Voir: jsperf.com/alternative-isfunction-implementations
Paul Rosania
13
J'aime bien le soulignement, simple mais puissant. Cela dit, il est probablement intéressant de noter que cette implémentation pourrait être usurpée par un objet avec ces attributs.
studgeek
3
@PaulRosania Nous sommes tombés sur ces tests de performances plus tôt dans la journée et les avons mis à jour avec une vérification et plus de données de test en tant que révision 4 - veuillez l'exécuter pour augmenter la couverture des tests du navigateur. Il est plus lent dans les opérations par seconde (en raison des nombreux tests), mais il montre également une différence de mise en œuvre (ouvrez votre console javascript et rechargez la page, il peut y avoir des messages d'erreur enregistrés). Remarque: la révision 3 ajoutée est isFunctionD (basée uniquement sur typeof == "function") - et elle semble être beaucoup plus rapide que la version "rapide" d'Underscore.
Joel Purra
6
Cette réponse est un peu trompeuse maintenant, car Underscore.js est maintenant sur la révision 12 , et non la révision 4 susmentionnée, de son test d' isFunction()implémentations alternatives . Il utilise actuellement quelque chose de très proche de ce que le dandin a suggéré.
Jonathan Eunice
112

Il y a plusieurs façons donc je vais les résumer toutes

  1. Le meilleur moyen est:
    function foo (v) {if (v instanceof Function) {/ * faire quelque chose * /}};
    
    
    Solution la plus performante (pas de comparaison de chaînes) et élégante - l'opérateur instanceof est pris en charge par les navigateurs depuis très longtemps, alors ne vous inquiétez pas - cela fonctionnera dans IE 6.
  2. Le meilleur moyen suivant est:
    fonction foo (v) {if (typeof v === "fonction") {/ * faire quelque chose * /}};
    
    
    L'inconvénient typeofest qu'il est susceptible d'échec silencieux, mauvais, donc si vous avez une faute de frappe (par exemple "finction") - dans ce cas, le "if" retournera juste faux et vous ne saurez que vous avez une erreur que plus tard dans votre code
  3. La deuxième meilleure façon est:
    function isFunction (functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call (functionToCheck) === '[Fonction objet]';
    }
    
    
    Cela n'a aucun avantage sur la solution # 1 ou # 2 mais est beaucoup moins lisible. Une version améliorée de ceci est
    function isFunction (x) {
        return Object.prototype.toString.call (x) == '[Fonction objet]';
    }
    
    
    mais toujours beaucoup moins sémantique que la solution # 1
dalimian
la source
10
La première solution échoue dans le cas d'une fonction passée à un contexte de trame différent. Par exemple, à partir d'un iframe top.Function !== Function. Pour être sûr, utilisez le second (toute faute d'orthographe de "fonction" sera corrigée lors du débogage, espérons-le).
MaxArt
Concernant votre point sur les fautes de frappe: tout code qui a cette faute de frappe est susceptible d'échouer rapidement / n'est pas plus apparent que tout autre bogue basé sur des fautes de frappe. typeof === "fonction" est la solution la plus propre. De plus, votre «meilleure» solution où vous utilisez instanceofne prend pas en compte plusieurs globaux, comme une vérification sur plusieurs cadres, et peut renvoyer des faux négatifs.
brainkim du
84

jQuery (obsolète depuis la version 3.3) Référence

$.isFunction(functionName);

Référence AngularJS

angular.isFunction(value);

Référence Lodash

_.isFunction(value);

Référence de soulignement

_.isFunction(object); 

Node.js déconseillé depuis la référence v4.0.0

var util = require('util');
util.isFunction(object);
Cesar Loachamin
la source
56

@grandecomplex: Votre solution est assez verbeuse. Ce serait beaucoup plus clair s'il était écrit comme ceci:

function isFunction(x) {
  return Object.prototype.toString.call(x) == '[object Function]';
}
dandean
la source
Object.prototype.toString.call est utile pour d'autres types de javascript qui vous intéressent. Vous pouvez même vérifier la valeur nulle ou non définie, ce qui le rend très puissant.
Jason Foglia
49

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

wsc
la source
1
... et (() => (typeof obj === 'function') && doSomething())reste l'option la plus rapide.
darcher
35

Essayez l' instanceofopérateur: il semble que toutes les fonctions héritent de la 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
Alexander Abakumov
la source
19

Quelque chose avec plus de support du navigateur et qui inclut également des fonctions asynchrones pourrait être:

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

puis testez-le comme:

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
Alireza
la source
11

Une autre manière simple:

var fn = function () {}
if (fn.constructor === Function) {
  // true
} else {
  // false
}
GuillaumeL
la source
1
si fn n'est pas défini, il générera une erreur
Kamil Orzechowski
fn && fn.constructor === Functionque dis-tu de ça?
Yaroslav Melnichuk
9

Pour ceux qui s'intéressent au style fonctionnel ou qui recherchent une approche plus expressive à utiliser dans la méta-programmation (comme la vérification de type), il pourrait être intéressant de voir la bibliothèque Ramda pour accomplir une telle tâche.

Le code suivant ne contient que des fonctions pures et sans point:

const R = require('ramda');

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

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

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

Depuis ES2017, des asyncfonctions sont disponibles, nous pouvons donc également les vérifier:

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

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

Et puis combinez-les ensemble:

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

Bien sûr, la fonction doit être protégé contre nullet undefinedvaleurs, afin de le rendre « sûr »:

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

Et, un extrait complet pour résumer:

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 ));

Cependant, notez que cette solution pourrait afficher moins de performances que les autres options disponibles en raison de l'utilisation intensive de fonctions d'ordre supérieur.

Organique endommagé
la source
7

Si vous utilisez Lodash, vous pouvez le faire avec _.isFunction .

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

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

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

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

Cette méthode retourne truesi value est une fonction, sinon false.

slorenzo
la source
4

J'ai trouvé que lors du test des fonctions de navigateur natif dans IE8, en utilisant toString, instanceofet typeofne fonctionne pas. Voici une méthode qui fonctionne bien dans IE8 (pour autant que je sache):

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

Alternativement, vous pouvez vérifier les fonctions natives en utilisant:

"getElementById" in document

Cependant, j'ai lu quelque part que cela ne fonctionnera pas toujours dans IE7 et ci-dessous.

Azmisov
la source
4

Ce qui suit semble également fonctionner pour moi (testé depuis node.js):

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

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false
Marcus Junius Brutus
la source
4

Je pense que vous pouvez simplement définir un indicateur sur le prototype de fonction et vérifier si l'instance que vous souhaitez tester a hérité de celle

définir un drapeau:

Function.prototype.isFunction = true; 

puis vérifiez s'il existe

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

L'inconvénient est qu'un autre prototype peut définir le même drapeau et que cela ne vaut rien, mais si vous avez un contrôle total sur les modules inclus, c'est le moyen le plus simple

Danny Mor
la source
Il échouera avec Error si foo n'est pas défini. Sans mentionner que vous modifiez le prototype natif, ce qui est totalement faux.
Kamil Orzechowski
3

Depuis le noeud v0.11, vous pouvez utiliser la fonction util standard:

var util = require('util');
util.isFunction('foo');
moklick
la source
2
util.isFunction est obsolète
kehers
2

vous devez utiliser l' typeOfopérateur dans js.

var a=function(){
    alert("fun a");
}
alert(typeof a);// alerts "function"
Rajesh Paul
la source
0

La solution, comme certaines réponses précédentes l'ont montré, consiste à utiliser typeof. ce qui suit est un extrait de code dans 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]();
  }
Badr Bellaj
la source