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.