Jak stwierdzić, czy zdefiniowano funkcję JavaScript


302

Jak rozpoznać, czy funkcja w JavaScript jest zdefiniowana?

Chcę zrobić coś takiego

function something_cool(text, callback) {
    alert(text);
    if( callback != null ) callback();
}

Ale to mnie rozumie

oddzwanianie nie jest funkcją

błąd, gdy wywołanie zwrotne nie jest zdefiniowane.

Odpowiedzi:


475
typeof callback === "function"

47
To nie jest tak naprawdę magiczny ciąg, ponieważ zestaw wartości typeof jest dokładnie zdefiniowany w specyfikacji ECMA. Chociaż składnia tego jest na początku trochę głupia, jest to całkowicie rozsądne idiomatyczne JavaScript.
Ben Zotto

2
@quixoto - zrozumiałe, myślę, że mam na myśli to, że mimo wszystko musisz mieć ciąg znaków - wolałbym, aby nie dosięgały mnie bardziej dosłowne ciągi, niż absolutnie potrzebne; dlatego nie jest to mój idealny do testowania, czy coś jest funkcją.
Jason Bunting,

4
I tak, „magia” była niechlujnym, kiepskim wyborem słów - miałem na myśli „literalny ciąg”, a nie „magiczny ciąg”. Mój błąd. :)
Jason Bunting


Upewnij się, że usunąłeś nawias w swojej funkcji i użyj nazwy funkcji tylko w warunku if, w przeciwnym razie funkcja zostanie wywołana zamiast podania wartości true lub false.
Russell Strauss

236

Wszystkie obecne odpowiedzi używają dosłownego ciągu, którego wolę nie mieć w kodzie, jeśli to możliwe - to nie (i zapewnia cenne znaczenie semantyczne, aby uruchomić):

function isFunction(possibleFunction) {
  return typeof(possibleFunction) === typeof(Function);
}

Osobiście staram się zmniejszyć liczbę ciągów znaków w moim kodzie ...


Ponadto, chociaż zdaję sobie sprawę, że typeofjest to operator, a nie funkcja, korzystanie ze składni, która sprawia, że ​​wygląda ona na tę drugą, jest niewielkie.


7
Jak mam korzystać z tej funkcji? Próbowałem, isFunction(not_defined))gdzie not_definedjest nazwa funkcji, która nie jest zdefiniowana i FireBug zwrócił, not_defined is not definedco jest poprawne, ale twoja funkcja powinna zwrócić wartość false i nie generować błędu ...
Wookie88,

@ Wookie88: Technicznie rzecz biorąc, funkcja zilustrowana w mojej odpowiedzi nie zwraca błędu tak bardzo, jak występuje błąd, ponieważ interpreter JavaScript próbuje rozwiązać symbol not_defined, aby przekazać jego wartość isFunction()i nie może go rozwiązać. Nic nie można zrobić z definicją isFunction()rozwiązania tego problemu.
Jason Bunting

3
@ZacharyYates Sposób obejścia błędu ReferenceError oznaczałby unikanie pracy z odwołaniem do funkcji, która może nie została zdefiniowana. Możesz wykonać kontrolę typu wewnątrz wywołania funkcji i przekazać wynik do funkcji. jsfiddle.net/qNaxJ Może nie tak fajnie, ale przynajmniej nie używa żadnych ciągów;)
net

8
Lub unikaj funkcji isFunctioni rób tylko (typeof no_function == typeof Function).
netto

2
@JasonBunting Jeśli będziesz tak wybredny, naprawdę powinieneś go używać, typeof(Function())ponieważ Function jest funkcją; ale podobnie jak Array, Object i inne wbudowane prototypy (z braku lepszego terminu). Jeśli sprawdzałeś tablicę, której typeof(array) === typeof(Array)na przykład nie używałbyś ; użyjesz tego, typeof(array) === typeof(Array())ponieważ faktycznie musisz ocenić funkcję, jeśli jesteś ostrożny i konsekwentny.
seanmhanson

16
if (callback && typeof(callback) == "function")

Należy zauważyć, że wywołania zwrotnego (przez siebie) ma wartość false, jeśli jest undefined, null, 0, lub false. Porównywanie do nulljest zbyt specyficzne.


2
Zauważ też, że jeśli callbacksama wartość ma wartość „fałsz-y”, to jej typof nigdy nie może być „funkcją”.
Joe

5
@Joe, ale z powodu zwarcia test ten nie zostanie przeprowadzony, jeśli wywołanie zwrotne będzie miało wartość false.
Fabio A.

3
to rozwiązanie nie działa, ponieważ pierwszy warunek natychmiast spowoduje wyjątek. to może działać, dla właściwości obiektu: if (obj.callback) {...}
Roey

8

Te metody określania, czy funkcja jest zaimplementowana, również zawodzą, jeśli zmienna nie jest zdefiniowana, dlatego używamy czegoś mocniejszego, który obsługuje otrzymywanie łańcucha:

function isFunctionDefined(functionName) {
    if(eval("typeof(" + functionName + ") == typeof(Function)")) {
        return true;
    }
}

if (isFunctionDefined('myFunction')) {
    myFunction(foo);
}

Myślę, że to dobra odpowiedź, masz rację, wszystkie powyższe metody zawodzą, funkcja nie jest zdefiniowana.
Matteo Conta

Trywialne jest unikanie niezdefiniowanych odniesień - wystarczy odwołać się do nich typeof window.doesthisexistzamiast doesthisexist. Jednak użycie eval (która: jest zła), jak pokazano, będzie działać tylko z nazwanymi funkcjami - nie działałoby to w przypadku użycia w pytaniu.
AD7six

Należy pamiętać, że działa to tylko w zakresie globalnym, podczas gdy zaakceptowana odpowiedź działa również z funkcjami zakresu lokalnego.
inf3rno

6

Nowość w JavaScript Nie jestem pewien, czy zachowanie się zmieniło, ale rozwiązanie podane przez Jasona Buntinga (6 lat temu) nie będzie działać, jeśli to możliwe Funkcja nie jest zdefiniowana.

function isFunction(possibleFunction) {
  return (typeof(possibleFunction) == typeof(Function));
}

Spowoduje to wygenerowanie ReferenceError: possibleFunction is not definedbłędu, gdy silnik spróbuje rozwiązać możliwy symbol Funkcja (jak wspomniano w komentarzach do odpowiedzi Jasona)

Aby uniknąć tego zachowania, możesz przekazać tylko nazwę funkcji, którą chcesz sprawdzić, czy istnieje. Więc

var possibleFunction = possibleFunction || {};
if (!isFunction(possibleFunction)) return false;

Ustawia zmienną na funkcję, którą chcesz sprawdzić, lub pusty obiekt, jeśli nie jest zdefiniowany, dzięki czemu unika się wyżej wymienionych problemów.


Aby być całkowicie jasnym: mój kod nie zgłasza błędu, robi to tylko parser JavaScript. To samo miałoby miejsce, gdybyś spróbował użyć dowolnego niezdefiniowanego identyfikatora w taki sposób.
Jason Bunting,

6

Próbować:

if (typeof(callback) == 'function')

HI, nie używaj porównania „luźnego” (==) zawsze używaj „===”, aby porównać nawet typ zmiennej.
Joel Carneiro

4
function something_cool(text, callback){
    alert(text);
    if(typeof(callback)=='function'){ 
        callback(); 
    };
}

4
if ('function' === typeof callback) ...

3
Nieco pedantyczny i nadal korzysta z ciągiem znaków. Jak o if(typeof Function === typeof callback)...? :)
Jason Bunting

@JasonBunting Ciekawe, co jest złego w używaniu literału?
Bsienn

@Bsienn - jak powiedziałem, to pedantycznie mnie, ale o to chodzi. Załóżmy, że używasz kodu z literałem ciągu, a później wywoływane jest typeofcoś innego (być może „Funkcja” zamiast „funkcja”), z powodu nowej / innej implementacji JavaScript - wydaje się , że mniej prawdopodobne jest, że nowa / inna implementacja złamałaby typeof Function === typeof callbackkontrolę, ponieważ powinna być taka sama na poziomie semantycznym.
Jason Bunting


4

mogę zrobić

try{
    callback();
}catch(e){};

Wiem, że istnieje akceptowana odpowiedź, ale nikt tego nie sugerował. Nie jestem do końca pewien, czy pasuje to do opisu idiomatic, ale działa we wszystkich przypadkach.

W nowszych silnikach JavaScript finallymożna zamiast tego użyć.


To fajny skrót! Chociaż nie będzie działać tylko do testowania, czy funkcja istnieje lub jest zdefiniowana (bez jej wykonania).
Beejor

To prawda. Przyjąłem sytuację, w której wywołanie zwrotne jest opcjonalne w tym punkcie wykonania. I typeof callbacktak zwykle używam .
Quentin Engles

To ma sens; Skupiłem się na tytule pytania bardziej niż na jego treści. Czasami podoba mi się prostota użycia try-catch na wiele testów. Czy uważasz, że korzystanie z niego ma wadę techniczną, w tym przypadku w porównaniu do czegoś takiego jak typeof? Czy jest to głównie kwestia robienia rzeczy w bardziej właściwy / oczekiwany sposób?
Beejor

1
Jeśli chcesz przetestować wiele typów, wyjątki nie są tak dobre. Na przykład projekt, nad którym teraz pracuję, wykorzystuje wiele sprawdzania typu w jednej zmiennej, aby sprawdzić, czy jest to funkcja, ciąg znaków lub cokolwiek innego. W tym projekcie naprawdę potrzebuję typów. W tym celu używam tablicy typów i sprawdzam, accepted_types.indexOf(typeof value) !== -1czy jest w zakresie typów. W tej sytuacji wyjątki byłyby moim zdaniem wygórowane.
Quentin Engles

2

Spróbuj tego:

callback instanceof Function

1
Nie działa Nadal da ci błąd. Powinieneś spróbować sam przed opublikowaniem go jako odpowiedzi.
vbullinger

@vbullinger Właśnie przetestowałem ponownie w Chrome, Firefox i Safari - działa wszędzie. Z którym silnikiem miałeś problemy?
eWolf,

Firefox Czy przetestowałeś przypadek, gdy nie zdefiniowano wywołania zwrotnego?
vbullinger

Jest tak, ponieważ bezpośrednio odwołujesz się do niezdefiniowanej zmiennej, która nigdy nie działa: pastebin.com/UQz2AmzH
eWolf

3
Działa, gdy jest używany z parametrem funkcji, o co prosił autor - Przypuszczam, że ECMAScript rozróżnia przypadki nieistniejącej zmiennej i istniejącej zmiennej o wartości niezdefiniowanej. Byłoby interesujące dowiedzieć się więcej na ten temat.
eWolf,

2

Jeśli spojrzysz na źródło biblioteki @Venkat Sudheer Reddy Aedama wspomniane, underscorejs, możesz zobaczyć:

_.isFunction = function(obj) {
  return typeof obj == 'function' || false;
};

To tylko moja WSKAZÓWKA, WSKAZÓWKA odpowiedź:>




1

Szukałem, jak sprawdzić, czy funkcja jQuery została zdefiniowana i nie znalazłem jej łatwo.

Być może może tego potrzebować;)

if(typeof jQuery.fn.datepicker !== "undefined")

tak jak type0f($.datepicker) !== undefiledinaczej będzieobject
vladkras,

0

Jeśli callback()wywołujesz nie tylko raz w funkcji, możesz zainicjować argument do ponownego użycia:

callback = (typeof callback === "function") ? callback : function(){};

Na przykład:

function something_cool(text, callback) {
    // Initialize arguments
    callback = (typeof callback === "function") ? callback : function(){};

    alert(text);

    if (text==='waitAnotherAJAX') {
        anotherAJAX(callback);
    } else {
        callback();
    }
}

Ograniczeniem jest to, że zawsze wykona argument wywołania zwrotnego, chociaż jest niezdefiniowany.


0

W przypadku funkcji globalnych możesz użyć tej zamiast evalsugerowanej w jednej z odpowiedzi.

var global = (function (){
    return this;
})();

if (typeof(global.f) != "function")
    global.f = function f1_shim (){
        // commonly used by polyfill libs
    };

Możesz także użyć global.f instanceof Function, ale afaik. wartość Functionbędzie różna w różnych ramkach, więc będzie działać poprawnie tylko z aplikacją na pojedynczą ramkę. Dlatego zwykle używamy typeofzamiast tego. Zauważ, że w niektórych środowiskach mogą również występować anomalie typeof f, np. Przez MSIE 6-8 niektóre funkcje na przykładalert miały typ „obiektowy”.

Za pomocą funkcji lokalnych możesz użyć tej z zaakceptowanej odpowiedzi. Możesz sprawdzić, czy funkcja jest lokalna czy globalna.

if (typeof(f) == "function")
    if (global.f === f)
        console.log("f is a global function");
    else
        console.log("f is a local function");

Aby odpowiedzieć na pytanie, przykładowy kod działa dla mnie bez błędów w najnowszych przeglądarkach, więc nie jestem pewien, z czym był problem:

function something_cool(text, callback) {
    alert(text);
    if( callback != null ) callback();
}

Uwaga: użyłbym callback !== undefinedzamiast callback != null, ale robią prawie to samo.


0

Większość, jeśli nie wszystkie poprzednie odpowiedzi, wywołują funkcję uboczną

tutaj najlepsza praktyka

masz funkcję

function myFunction() {
        var x=1;
    }
bezpośredni sposób na przetestowanie go

//direct way
        if( (typeof window.myFunction)=='function')
            alert('myFunction is function')
        else
            alert('myFunction is not defined');
za pomocą ciągu znaków, dzięki czemu możesz mieć tylko jedno miejsce do zdefiniowania nazwy funkcji

//byString
        var strFunctionName='myFunction'
        if( (typeof window[strFunctionName])=='function')
            alert(s+' is function');
        else
            alert(s+' is not defined');


0

Jeśli chcesz przedefiniować funkcje, najlepiej użyć zmiennych funkcyjnych, które są zdefiniowane w kolejności ich występowania, ponieważ funkcje są definiowane globalnie, bez względu na to, gdzie występują.

Przykład tworzenia nowej funkcji, która wywołuje poprzednią funkcję o tej samej nazwie:

A=function() {...} // first definition
...
if (typeof A==='function')
   oldA=A;
A=function() {...oldA()...} // new definition


-2

Rozwiązanie jednowierszowe:

function something_cool(text, callback){
    callback && callback();
}

Jego przykład pokazuje tylko, że chce, aby funkcja została wywołana, jeśli jest zdefiniowana, a jeśli nie, nic się nie dzieje. jsfiddle.net/nh1cxxg6 Funkcje, które zwracają wartości falsey / false nadal są wywoływane. Oczywiście nie działa to, gdy chcesz zwrócić wartości.
Samir Alajmovic

Ach, źle odczytałem jego pytanie. Spowodowałoby to jednak wyjątek, jeśli wywołanie zwrotne nie jest funkcją.
Frank Bryce,
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.