Controlla se una variabile è di tipo funzione


904

Supponiamo che io abbia una variabile, che è definita come segue:

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

Voglio una funzione che controlla se il tipo di variabile è simile alla funzione. cioè:

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

Come posso verificare se la variabile aè di tipo Functionnel modo sopra definito?


2
qui un punto di riferimento per questi modi più comuni per controllare questo jsben.ch/#/e5Ogr
EscapeNetscape

Risposte:


380

Il modo in cui il carattere di sottolineatura è più efficiente, ma il modo migliore per verificare, quando l'efficienza non è un problema, è scritto sulla pagina del carattere di sottolineatura collegata da @Paul Rosania.

Ispirato dal trattino basso, la funzione finale isFunction è la seguente:

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

1
Potrebbe esserci qualcuno che ha fatto ricerche più approfondite sull'argomento, ma dai un'occhiata ai risultati della revisione 4 di jsperf con una semplice "verifica dei risultati" . isFunctionAmostra una differenza di implementazione rispetto agli altri metodi.
Joel Purra,

24
Con i test delle prestazioni aggiornati sembra che ci sia un'enorme differenza di velocità a seconda del browser. In Chrome typeof(obj) === 'function'sembra essere di gran lunga il più veloce; tuttavia, in Firefox obj instanceof Functionè il chiaro vincitore.
Justin Warkentin,

7
Per quanto riguarda la parte: typeof dovrebbe essere usato solo per verificare se variabili o proprietà non sono definite. A javascript.info/tutorial/type-detection nella sezione Un buon uso di typeof e in quella successiva l'autore afferma che il caso è esattamente l'opposto
Konrad Madej,

11
Alex, sono curioso di sapere perché dici che typeof dovrebbe essere usato solo per verificare che non sia definito. Il suo nome suggerisce che il suo scopo è quello di controllare il tipo di qualcosa, non solo se esiste. Ci sono problemi tecnici o problemi che possono sorgere quando lo si utilizza per eseguire il controllo del tipo o è più una preferenza? Se ci sono avvertimenti sarebbe bene elencarli in modo che altri possano evitare di inciampare su di essi.
jinglesthula,

14
soluzione irragionevolmente complicata
Daniel Garmoshka,

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

44
Basta guardare fuori per un paio di cose come typeof Object, typeof Datee typeof Stringche tutto il ritorno 'function'troppo.
Dave Ward,

359
@Dave, non sono sicuro di quale sia il problema, dal momento che quelle sono funzioni.
Matthew Crumley,

6
Qual è la differenza con if (v instanceOf Function)?
mquandalle,

10
@mquandalle Nessuna grande differenza, tranne instanceofche non funzionerà se stai controllando una funzione proveniente da un altro documento (un iframe, forse). Le prestazioni variano.
rvighne,

8
a differenza della risposta accettata, questo funziona anche per le funzioni asincrone. Per un asyncfunctionToCheck, getType.toString.call(functionToCheck)==='[object AsyncFunction]'dove cometypeof asyncfunctionToCheck === 'function'
TDreama

132

Underscore.js utilizza un test più elaborato ma altamente performante:

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

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

EDIT: test aggiornati suggeriscono che typeof potrebbe essere più veloce, vedi http://jsperf.com/alternative-isfunction-implementations/4


C'è qualche motivo particolare per utilizzare questo approccio rispetto a typof?
sv_in

1
La versione di Underscore è molto più veloce nella maggior parte dei browser. Vedi: jsperf.com/alternative-isfunction-implementations
Paul Rosania

13
Sicuramente mi piace il carattere di sottolineatura, semplice ma potente. Detto questo, probabilmente vale la pena notare che questa implementazione potrebbe essere falsificata da un oggetto con quegli attributi.
Studgeek,

3
@PaulRosania Si è imbattuto in questi test delle prestazioni prima di oggi e li ha aggiornati con la verifica più altri dati di test come revisione 4 - eseguilo per aumentare la copertura dei test del browser. È più lento nelle operazioni al secondo (a causa dei numerosi test), ma mostra anche una differenza di implementazione (apri la tua console javascript e ricarica la pagina, potrebbero esserci dei messaggi di errore registrati). Nota: la Revisione 3 ha aggiunto isFunctionD (basato solo su typeof == "function") e sembra essere molto più veloce della versione "veloce" di Underscore.
Joel Purra,

6
Questa risposta è un po 'fuorviante ora, poiché Underscore.js è ora sulla revisione 12 , non sulla revisione 4 sopra menzionata, del suo test di isFunction()implementazioni alternative . Attualmente utilizza qualcosa di molto simile a quello suggerito da Dandean .
Jonathan Eunice

112

Esistono diversi modi, quindi li riassumerò tutti

  1. Il modo migliore è:
    function foo (v) {if (v instanceof Function) {/ * fare qualcosa * /}};
    
    
    La soluzione più performante (nessun confronto di stringhe) ed elegante - l'operatore instanceof è supportato nei browser da molto tempo, quindi non preoccuparti - funzionerà in IE 6.
  2. Il prossimo modo migliore è:
    function foo (v) {if (typeof v === "function") {/ * do qualcosa * /}};
    
    
    svantaggio di typeof è che è suscettibile a fallimento silenzioso, cattivo, quindi se hai un errore di battitura (ad esempio "fiction") - in questo caso `if` restituirà solo falso e non saprai di avere un errore fino a dopo in il tuo codice
  3. Il prossimo modo migliore è:
    function isFunction (functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call (functionToCheck) === '[Funzione oggetto]';
    }
    
    
    Questo non ha alcun vantaggio rispetto alla soluzione n. 1 o n. 2, ma è molto meno leggibile. Una versione migliorata di questo è
    funzione isFunction (x) {
        return Object.prototype.toString.call (x) == '[Funzione oggetto]';
    }
    
    
    ma molto meno semantico della soluzione n. 1

10
La prima soluzione ha esito negativo in caso di una funzione passata a un contesto di frame diverso. Ad esempio, da un iframe top.Function !== Function. A dire il vero, usa il secondo (ogni errore di ortografia di "funzione" sarebbe corretto durante il debug, si spera).
MaxArt

Per quanto riguarda il tuo punto di vista sugli errori di battitura: qualsiasi codice che ha questo errore di battitura rischia di fallire rapidamente / non è più evidente di qualsiasi altro bug basato su errori di battitura. typeof === "function" è la soluzione più pulita. Inoltre, la tua "migliore" soluzione in cui utilizzi instanceofnon tiene conto di più globi, come un controllo tra frame, e può restituire falsi negativi.
brainkim,


56

@grandecomplex: c'è una buona dose di verbosità nella tua soluzione. Sarebbe molto più chiaro se scritto in questo modo:

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

Object.prototype.toString.call è utile per altri tipi di javascript che ti interessano. Puoi anche controllare contro null o undefined che lo rende molto potente.
Jason Foglia,

49

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


1
... ed (() => (typeof obj === 'function') && doSomething())è ancora l'opzione più veloce.
Darcher,

35

Prova l' instanceofoperatore: sembra che tutte le funzioni ereditino dalla 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

Qualcosa con più supporto del browser e include anche funzioni asincrone potrebbe essere:

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

e poi testalo come:

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

11

Un altro modo semplice:

var fn = function () {}
if (fn.constructor === Function) {
  // true
} else {
  // false
}

1
se fn sarà indefinito, verrà generato un errore
Kamil Orzechowski,

fn && fn.constructor === Functioncosa ne pensi di questo?
Yaroslav Melnichuk

9

Per coloro che sono interessati allo stile funzionale o cercano un approccio più espressivo da utilizzare nella meta-programmazione (come il controllo del tipo), potrebbe essere interessante vedere Ramda libreria per svolgere tale compito.

Il codice successivo contiene solo funzioni pure e inutili:

const R = require('ramda');

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

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

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

A partire da ES2017, le asyncfunzioni sono disponibili, quindi possiamo anche verificarle:

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

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

E poi combinali insieme:

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

Naturalmente, la funzione deve essere protetta contro nulle undefinedvalori, quindi per renderla "sicura":

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

E, completare lo snippet per riassumere:

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

Tuttavia, si noti che questa soluzione potrebbe mostrare prestazioni inferiori rispetto ad altre opzioni disponibili a causa dell'ampio utilizzo di funzioni di ordine superiore.


7

Se usi Lodash puoi farlo con _.isFunction .

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

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

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

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

Questo metodo restituisce truese value è una funzione, altrimenti false.


4

Ho scoperto che durante il test funzioni native del browser in IE8, usando toString, instanceofe typeofnon ha funzionato. Ecco un metodo che funziona perfettamente in IE8 (per quanto ne so):

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

In alternativa, puoi controllare le funzioni native usando:

"getElementById" in document

Tuttavia, ho letto da qualche parte che questo non funzionerà sempre in IE7 e sotto.


4

Quanto segue sembra funzionare anche per me (testato da node.js):

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

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

4

Penso che puoi semplicemente definire un flag sul prototipo Function e verificare se l'istanza che vuoi testare ha ereditato

definire una bandiera:

Function.prototype.isFunction = true; 

e poi controlla se esiste

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

Il rovescio della medaglia è che un altro prototipo può definire la stessa bandiera e quindi è inutile, ma se hai il pieno controllo sui moduli inclusi è il modo più semplice


Fallirà con Errore se foo non è definito. Senza contare che si modifica il prototipo nativo che è totalmente sbagliato.
Kamil Orzechowski,

3

Dal nodo v0.11 è possibile utilizzare la funzione util standard:

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

2
util.isFunction è deprecato
kehers

2

dovresti usare l' typeOfoperatore in js.

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

0

La soluzione, come hanno mostrato alcune risposte precedenti, è usare typeof. il seguente è uno snippet di codice in 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]();
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.