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?


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]';
}

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
}

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


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

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,


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]';
}

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")
}


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

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


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.


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.


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.


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

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


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

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"

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]();
  }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.