Sprawdź, czy zmienna jest typu funkcji

903

Załóżmy, że mam dowolną zmienną, która jest zdefiniowana w następujący sposób:

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

Chcę funkcji, która sprawdza, czy typ zmiennej jest podobny do funkcji. tj .:

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

Jak mogę sprawdzić, czy zmienna ajest typu Functionw sposób określony powyżej?

Jesufer Vn
źródło
2
tutaj punkt odniesienia dla tych najczęstszych sposobów sprawdzenia tego jsben.ch/#/e5Ogr
EscapeNetscape 25.10.16

Odpowiedzi:

380

Oczywiście sposób podkreślenia jest bardziej wydajny, ale najlepszy sposób sprawdzenia, kiedy wydajność nie jest problemem, jest napisany na stronie podkreślonej przez @Paul Rosania.

Zainspirowany podkreśleniem, końcowa funkcja isFunction jest następująca:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
Alex Grande
źródło
1
Może być ktoś, kto przeprowadził szersze badania w tej sprawie, ale spójrz na wyniki wersji 4 jsperf z prostą „weryfikacją wyników” . isFunctionApokazuje różnicę w porównaniu do innych metod.
Joel Purra
24
Dzięki zaktualizowanym testom wydajności wygląda na to, że istnieje ogromna różnica prędkości w zależności od przeglądarki. W Chrome typeof(obj) === 'function'wydaje się zdecydowanie najszybszy; jednak w Firefox obj instanceof Functionjest wyraźnym zwycięzcą.
Justin Warkentin
7
W odniesieniu do części: typeof powinien być używany tylko do sprawdzania, czy zmienne lub właściwości są niezdefiniowane. Na javascript.info/tutorial/type-detection w sekcji Dobre wykorzystanie typeof i następnej autor stwierdza, że ​​sprawa jest dokładnie odwrotna
Konrad Madej
11
Alex, jestem ciekawy, dlaczego mówisz, że typeof powinien być używany tylko do sprawdzania nieokreślonego. Jego nazwa sugeruje, że jego celem jest sprawdzenie rodzaju czegoś, a nie tylko tego, czy istnieje. Czy występują problemy techniczne lub problemy, które mogą pojawić się podczas korzystania z niego do sprawdzania typu, czy jest to raczej preferencja? Jeśli istnieją zastrzeżenia, dobrze byłoby je wymienić, aby inni mogli uniknąć potknięcia się o nie.
jinglesthula
14
nieuzasadnione nadmiernie skomplikowane rozwiązanie
Daniel Garmoshka
1710
if (typeof v === "function") {
    // do something
}
Selbie
źródło
44
Wystarczy zwrócić uwagę na kilka rzeczy, takich jak typeof Object, typeof Datei typeof String, co wszyscy zwrot 'function'też.
Dave Ward
358
@Dave, nie jestem pewien, na czym polega problem, ponieważ są to funkcje.
Matthew Crumley
6
Jaka jest różnica if (v instanceOf Function)?
mquandalle 17.09.13
10
@mquandalle Nie ma dużej różnicy, z wyjątkiem tego, instanceofże nie zadziała, jeśli sprawdzasz funkcję pochodzącą z innego dokumentu (może to być element iframe). Wydajność jest różna.
rvighne
8
w przeciwieństwie do przyjętej odpowiedzi, działa to również w przypadku funkcji asynchronicznych. Na przykład asyncfunctionToCheck, getType.toString.call(functionToCheck)==='[object AsyncFunction]'gdzietypeof asyncfunctionToCheck === 'function'
TDreama,
132

Underscore.js używa bardziej złożonego, ale wysoce wydajnego testu:

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

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

EDYCJA: zaktualizowane testy sugerują, że typeof może być szybszy, patrz http://jsperf.com/alternative-isfunction-implementations/4

Paul Rosania
źródło
Czy jest jakiś konkretny powód, aby zastosować to podejście typof?
sv_in
1
Wersja podkreślenia jest znacznie szybsza w większości przeglądarek. Zobacz: jsperf.com/alternative-isfunction-implementations
Paul Rosania
13
Z pewnością lubię podkreślenie, proste, ale potężne. To powiedziawszy, prawdopodobnie warto zauważyć, że ta implementacja może zostać sfałszowana przez obiekt o tych atrybutach.
studgeek
3
@PaulRosania Natknąłem się dziś na te testy wydajności i zaktualizowałem je o weryfikację oraz dodatkowe dane testowe w wersji 4 - uruchom ją, aby zwiększyć zasięg testu przeglądarki. Jest wolniejszy w operacjach na sekundę (z powodu wielu testów), ale pokazuje także jedną różnicę implementacji (otwórz konsolę javascript i przeładuj stronę, mogą pojawić się komunikaty o błędach). Uwaga: Dodana wersja 3 to FunkcjaF (oparta tylko na typeof == "function") - i wydaje się być znacznie szybsza niż „szybka” wersja Underscore.
Joel Purra
6
Ta odpowiedź jest teraz nieco myląca, ponieważ Underscore.js jest teraz w wersji 12 , a nie w wyżej wspomnianej wersji 4 swojego testu alternatywnych isFunction()implementacji. Obecnie używa czegoś bardzo zbliżonego do tego, co sugerował dandean .
Jonathan Eunice
112

Istnieje kilka sposobów, dlatego streszczę je wszystkie

  1. Najlepszym sposobem jest:
    function foo (v) {if (v instanceof Function) {/ * zrób coś * /}};
    
    
    Najbardziej wydajne (bez porównania ciągów) i eleganckie rozwiązanie - operator instanceof jest obsługiwany w przeglądarkach od bardzo dawna, więc nie martw się - będzie działał w IE 6.
  2. Następnym najlepszym sposobem jest:
    function foo (v) {if (typeof v === "function") {/ * zrób coś * /}};
    
    
    wadą typeofjest to, że jest podatny na cichą awarię, źle, więc jeśli masz literówkę (np. „zakończenie”) - w tym przypadku „if” po prostu zwróci fałsz i nie będziesz wiedział, że masz błąd, dopóki nie Twój kod
  3. Kolejny najlepszy sposób to:
    funkcja isFunction (functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call (functionToCheck) === '[Object Function]';
    }
    
    
    Nie ma to przewagi nad rozwiązaniem nr 1 lub nr 2, ale jest o wiele mniej czytelne. Udoskonalona wersja tego
    funkcja isFunction (x) {
        return Object.prototype.toString.call (x) == '[funkcja obiektu]';
    }
    
    
    ale wciąż znacznie mniej semantyczny niż rozwiązanie nr 1
dalmatyńczyk
źródło
10
Pierwsze rozwiązanie kończy się niepowodzeniem w przypadku funkcji przekazanej do innego kontekstu ramki. Na przykład z elementu iframe top.Function !== Function. Dla pewności użyj drugiego (mam nadzieję, że wszelkie błędy pisowni „funkcji” zostaną poprawione podczas debugowania).
MaxArt
Jeśli chodzi o twoje zdanie na temat literówek: każdy kod, który ma tę literówkę, prawdopodobnie szybko zawiedzie / nie będzie bardziej widoczny niż jakiekolwiek inne błędy oparte na literówkach. typeof === „funkcja” jest najczystszym rozwiązaniem. Ponadto twoje „najlepsze” rozwiązanie, w którym używasz instanceof, nie bierze pod uwagę wielu globałów, takich jak sprawdzanie w różnych ramkach, i może zwracać fałszywe negatywy.
brainkim,
84

jQuery (przestarzałe od wersji 3.3) Odniesienie

$.isFunction(functionName);

Odniesienie AngularJS

angular.isFunction(value);

Referencje Lodash

_.isFunction(value);

Odniesienie do podkreślenia

_.isFunction(object); 

Node.js jest przestarzałe od wersji 4.0.0 Reference

var util = require('util');
util.isFunction(object);
Cesar Loachamin
źródło
56

@grandecomplex: W twoim rozwiązaniu jest sporo gadatliwości. Byłoby o wiele jaśniej, gdyby napisano w ten sposób:

function isFunction(x) {
  return Object.prototype.toString.call(x) == '[object Function]';
}
dandean
źródło
Object.prototype.toString.call jest przydatny dla innych typów javascript, którymi jesteś zainteresowany. Możesz nawet sprawdzić, czy nie ma wartości null lub undefined, co czyni go bardzo potężnym.
Jason Foglia
49

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

wsc
źródło
1
... i (() => (typeof obj === 'function') && doSomething())nadal jest najszybszą opcją.
darcher
35

Wypróbuj instanceofoperator: wygląda na to, że wszystkie funkcje dziedziczą po Functionklasie:

// 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
Aleksander Abakumow
źródło
19

Coś z większą obsługą przeglądarki i funkcjami asynchronicznymi może być:

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

a następnie przetestuj:

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
źródło
11

Innym prostym sposobem:

var fn = function () {}
if (fn.constructor === Function) {
  // true
} else {
  // false
}
GuillaumeL
źródło
1
jeśli fn będzie niezdefiniowany, wygeneruje błąd
Kamil Orzechowski
fn && fn.constructor === Functionco powiesz na to?
Yaroslav Melnichuk
9

Dla tych, którzy są zainteresowani stylem funkcjonalnym lub szukają bardziej wyrazistego podejścia do wykorzystania w metaprogramowaniu (takim jak sprawdzanie typów), interesujące może być obejrzenie biblioteki Ramda, aby wykonać takie zadanie.

Następny kod zawiera tylko czyste i bezcelowe funkcje:

const R = require('ramda');

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

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

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

Począwszy od ES2017, asyncfunkcje są dostępne, więc możemy je również sprawdzić:

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

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

A następnie połącz je razem:

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

Oczywiście funkcja powinna być chroniona przed wartościami nulli undefinedwartościami, aby była „bezpieczna”:

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

I uzupełnij fragment kodu, aby podsumować:

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

Należy jednak pamiętać, że to rozwiązanie może wykazywać mniejszą wydajność niż inne dostępne opcje z powodu szerokiego użycia funkcji wyższego rzędu.

Uszkodzony organiczny
źródło
7

Jeśli używasz Lodash, możesz to zrobić za pomocą _.isFunkcji .

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

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

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

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

Ta metoda zwraca, truejeśli wartość jest funkcją, w przeciwnym razie false.

slorenzo
źródło
4

Okazało się, że podczas testowania natywne funkcje przeglądarki w IE8, używając toString, instanceofi typeofnie działa. Oto metoda, która działa dobrze w IE8 (o ile mi wiadomo):

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

Alternatywnie możesz sprawdzić funkcje natywne, używając:

"getElementById" in document

Chociaż gdzieś przeczytałem, że nie zawsze będzie to działać w IE7 i niższych wersjach.

Azmisow
źródło
4

Poniższe wydaje się również działać dla mnie (testowane z node.js):

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

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false
Marcus Junius Brutus
źródło
4

Myślę, że możesz po prostu zdefiniować flagę na prototypie funkcji i sprawdzić, czy instancja, którą chcesz przetestować, odziedziczyła to

zdefiniuj flagę:

Function.prototype.isFunction = true; 

a następnie sprawdź, czy istnieje

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

Minusem jest to, że inny prototyp może zdefiniować tę samą flagę, a następnie jest bezwartościowy, ale jeśli masz pełną kontrolę nad dołączonymi modułami, jest to najprostszy sposób

Danny Mor
źródło
Błąd nie powiedzie się, jeśli foo jest niezdefiniowany. Nie wspominając o tym, że modyfikujesz natywny prototyp, co jest całkowicie błędne.
Kamil Orzechowski
3

Od węzła v0.11 możesz użyć standardowej funkcji util:

var util = require('util');
util.isFunction('foo');
moklick
źródło
2
util.isFunkcja jest przestarzała
kehers 28.01.16
2

powinieneś użyć typeOfoperatora w js.

var a=function(){
    alert("fun a");
}
alert(typeof a);// alerts "function"
Rajesh Paul
źródło
0

Rozwiązaniem, jak pokazały niektóre poprzednie odpowiedzi, jest użycie typeof. Oto fragment kodu W 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
źródło