Как определить, определена ли функция JavaScript

302

Как определить, определена ли функция в JavaScript?

Я хочу сделать что-то подобное

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

Но это заставляет меня

обратный вызов не является функцией

ошибка, когда обратный вызов не определен.

Аарон Ли
источник

Ответы:

475
typeof callback === "function"
Том Риттер
источник
47
На самом деле это не волшебная строка, поскольку набор значений typeof точно определен в спецификации ECMA. Хотя синтаксис этого для начала немного глуповат, это вполне разумный идиоматический Javascript.
Бен Зотто
2
@quixoto - понял, я думаю, я имею в виду, что вам все равно нужно иметь строку независимо - я бы предпочел, чтобы в моем коде не было больше буквенных строк, чем абсолютно необходимо; следовательно, это не мой идеал для тестирования, если что-то является функцией.
Джейсон Бантинг
4
И да, «магия» была небрежной, плохой выбор слова - я имел в виду «буквальная строка», а не «волшебная строка». Виноват. :)
Джейсон Бантинг
1
JSPerf - jsperf.com/…
Крис
Обязательно удалите круглые скобки в вашей функции и используйте только имя функции в условии if, иначе функция будет вызываться вместо того, чтобы давать вам true или false.
Рассел Штраус
236

Все текущие ответы используют литеральную строку, которую я предпочитаю не использовать в своем коде, если это возможно, - это не так (и обеспечивает ценное семантическое значение для загрузки):

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

Лично я стараюсь уменьшить количество строк, которые висят в моем коде ...


Кроме того, пока я знаю, что typeof это оператор, а не функция, использование синтаксиса делает его вредным для последнего.

Джейсон Бантинг
источник
7
Как я должен использовать эту функцию? Я пробовал, isFunction(not_defined))где not_definedимя функции, которая не определена, и FireBug вернул, not_defined is not definedчто является правильным, но ваша функция должна возвращать false и не генерировать ошибку ...
Wookie88 22.10.12
@ Wookie88: Технически говоря, функция, показанная в моем ответе, не возвращает ошибку так часто, как ошибка, потому что интерпретатор JavaScript пытается разрешить символ not_defined, чтобы передать его значение, isFunction()и не может его разрешить. Ничто не может быть сделано для определения isFunction()этой проблемы.
Джейсон Бантинг
3
@ZacharyYates Способ обойти ReferenceError означал бы избегать работы со ссылкой на возможно не определенную функцию. Вы можете выполнить проверку типов внутри вызова функции и передать результат в функцию. jsfiddle.net/qNaxJ Может быть, не очень хорошо, но, по крайней мере, без строк;)
netiul
8
Или избегайте функции isFunctionи делайте просто (typeof no_function == typeof Function).
нетил
2
@JasonBunting Если вы хотите быть разборчивым, вам действительно следует использовать его, typeof(Function())потому что Function - это функция; но так же как Array, Object и другие встроенные прототипы (из-за отсутствия лучшего термина). Если бы вы проверяли массив, вы бы не использовали его, typeof(array) === typeof(Array)например; вы будете использовать, typeof(array) === typeof(Array())потому что вам действительно нужно оценить функцию, если вы будете осторожны и последовательны.
Шенмхансон
16
if (callback && typeof(callback) == "function")

Обратите внимание , что обратный вызов (сам по себе) принимает значение , falseесли оно undefined, null, 0, или false. По сравнению с nullявляется слишком конкретным.

Робин как птица
источник
2
Также обратите внимание, что если callbackсамо значение равно false-y, то его typeof никогда не может быть «функцией».
Джо
5
@ Joe, но из-за короткого замыкания этот тест не будет выполнен, если обратный вызов оценивается как ложный.
Фабио А.
3
это решение не работает, потому что первое условие вызовет исключение немедленно. это может работать для свойства объекта: if (obj.callback) {...}
Роу
8

Эти методы, чтобы определить, реализована ли функция, также завершаются ошибкой, если переменная не определена, поэтому мы используем что-то более мощное, которое поддерживает получение строки:

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

if (isFunctionDefined('myFunction')) {
    myFunction(foo);
}
patriciorocca
источник
Я думаю, что это хороший ответ, вы правы, все вышеперечисленные методы не работает, функция не определена.
Маттео Конта
Избежать неопределенных ссылок тривиально - просто обратитесь typeof window.doesthisexistвместо doesthisexist. Однако использование eval (которое: является злом), как показано, будет работать только с именованными функциями - это не будет работать с вариантом использования в вопросе.
AD7six
Имейте в виду, что это работает только с глобальной областью действия, в то время как принятый ответ также работает с локальными функциями области действия.
inf3rno
6

Новое в JavaScript Я не уверен, изменилось ли поведение, но решение, данное Джейсоном Бантингом (6 лет назад), не будет работать, если возможная функция не определена.

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

Это вызовет ReferenceError: possibleFunction is not definedошибку, когда движок попытается разрешить символ возможной функции (как указано в комментариях к ответу Джейсона).

Чтобы избежать такого поведения, вы можете только передать имя функции, которую вы хотите проверить, если она существует. Так

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

Это устанавливает переменную либо как функцию, которую вы хотите проверить, либо как пустой объект, если она не определена, что позволяет избежать проблем, упомянутых выше.

NectarSoft
источник
Чтобы быть совершенно ясным: мой код не выдает ошибку, это делает только парсер JavaScript. То же самое произошло бы, если бы вы попытались использовать любой неопределенный идентификатор таким способом.
Джейсон Бантинг
6

Пытаться:

if (typeof(callback) == 'function')
bdukes
источник
Привет, не используйте «свободное» сравнение (==), всегда используйте «===» для сравнения даже типа переменной.
Жоэль Карнейру
4
function something_cool(text, callback){
    alert(text);
    if(typeof(callback)=='function'){ 
        callback(); 
    };
}
ConroyP
источник
4
if ('function' === typeof callback) ...
Эндрю Хеджес
источник
3
Немного педантичный, и до сих пор использует строковый литерал. Как насчет if(typeof Function === typeof callback)...? :)
Джейсон Бантинг
@JasonBunting Любопытно, что не так с использованием строкового литерала?
Bsienn
@Bsienn - как я уже сказал, это педантично с моей стороны, но вот в чем дело. Допустим, вы используете код со строковым литералом, и вызов typeofпозже возвращает что-то другое (может быть, «Function» вместо «function») из-за новой / другой реализации JavaScript - кажется, что это будет менее вероятно, что новая / другая реализация сломает typeof Function === typeof callbackпроверку, потому что она должна быть одинаковой на семантическом уровне.
Джейсон Бантинг
4

Я мог бы сделать

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

Я знаю, что есть принятый ответ, но никто не предложил это. Я не совсем уверен, соответствует ли это описанию idiomatic, но это работает для всех случаев.

В более новых движках JavaScript finallyвместо них можно использовать.

Квентин Энглс
источник
Это аккуратный ярлык! Хотя это не будет работать только для тестирования, если функция существует или определена (без ее выполнения).
Бежор
Это правда. Я предположил ситуацию, когда обратный вызов не является обязательным в этой точке выполнения. Я обычно использую в typeof callbackлюбом случае.
Квентин Энглс
В этом есть смысл; Я сосредоточился на названии вопроса больше, чем на его содержании. Иногда мне нравится простота использования try-catch для более чем тонны тестов. Как вы думаете, есть технический недостаток в его использовании, в данном случае, по сравнению с typeof? Или это в основном дело в том, чтобы делать что-то более правильное / ожидаемое?
Beejor
1
Если вы хотите проверить несколько типов, исключения не так хороши. Например, проект, над которым я сейчас работаю, использует много проверок типов для одной переменной, чтобы определить, является ли она функцией, строкой или чем-то еще. В этом проекте мне действительно нужны типы. Для этого я использую массив типов и проверяю, находится accepted_types.indexOf(typeof value) !== -1ли он в диапазоне типов. В этой ситуации исключения были бы непомерными, на мой взгляд.
Квентин Энглс
2

Попробуй это:

callback instanceof Function
eWolf
источник
1
Не работает Это все еще даст вам ошибку. Вы должны попробовать сами, прежде чем опубликовать ответ.
Vbullinger
@vbullinger Я только что снова протестировал в Chrome, Firefox и Safari - работает везде. С каким двигателем у вас возникли проблемы?
eWolf
Fire Fox. Вы тестировали случай, когда обратный вызов не был определен?
Vbullinger
Это потому, что вы напрямую ссылаетесь на неопределенную переменную, которая никогда не работает: pastebin.com/UQz2AmzH
eWolf
3
Он работает, когда используется с параметром функции, о чем просил автор - я полагаю, ECMAScript различает случаи несуществующей переменной и существующей переменной со значением undefined. Было бы интересно узнать больше об этом.
eWolf
2

Если вы посмотрите на источник упомянутой библиотеки @Venkat Sudheer Reddy Aedama, underscorejs, вы можете увидеть это:

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

Это только моя подсказка, подсказка:>

VentyCZ
источник
2

Пытаться:

if (!(typeof(callback)=='undefined')) {...}
Брайан
источник
1

Я искал, как проверить, была ли определена функция jQuery, и я не нашел ее легко.

Возможно, это может понадобиться;)

if(typeof jQuery.fn.datepicker !== "undefined")
miguelmpn
источник
так же, как type0f($.datepicker) !== undefiledиначе будетobject
vladkras
0

Если callback()вы вызываете функцию не один раз, вы можете инициализировать аргумент для повторного использования:

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

Например:

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

    alert(text);

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

Ограничение состоит в том, что он всегда будет выполнять аргумент обратного вызова, хотя он не определен.

Ник Цай
источник
0

Для глобальных функций вы можете использовать это вместо evalпредложенного в одном из ответов.

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

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

Вы можете использовать global.f instanceof Functionтакже, но afaik. значение Functionбудет различным в разных кадрах, поэтому оно будет работать только с одним приложением. Вот почему мы обычно используем typeofвместо этого. Обратите внимание, что в некоторых средах могут быть и аномалии typeof f, например, в MSIE 6-8 некоторые функции, например, alertимели тип «объект».

По локальным функциям вы можете использовать тот, что в принятом ответе. Вы можете проверить, является ли функция локальной или глобальной.

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

Чтобы ответить на вопрос, пример кода работает для меня без ошибок в последних браузерах, поэтому я не уверен, что с ним было:

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

Примечание: я бы использовал callback !== undefinedвместо callback != null, но они делают почти то же самое.

inf3rno
источник
0

Большинство, если не все предыдущие ответы имеют побочные эффекты для вызова функции

здесь лучшая практика

у тебя есть функция

function myFunction() {
        var x=1;
    }
прямой способ проверить это

//direct way
        if( (typeof window.myFunction)=='function')
            alert('myFunction is function')
        else
            alert('myFunction is not defined');
используя строку, чтобы вы могли иметь только одно место для определения имени функции

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

TexWiller
источник
0

Если вы хотите переопределить функции, лучше всего использовать переменные функций, которые определены в порядке их появления, поскольку функции определяются глобально, независимо от того, где они происходят.

Пример создания новой функции, которая вызывает предыдущую функцию с тем же именем:

A=function() {...} // first definition
...
if (typeof A==='function')
   oldA=A;
A=function() {...oldA()...} // new definition
Дэвид Спектор
источник
0

Это сработало для меня

if( cb && typeof( eval( cb ) ) === "function" ){
    eval( cb + "()" );
}
Патрик Огбуитепу
источник
-2

Однолинейное решение:

function something_cool(text, callback){
    callback && callback();
}
Самир Алаймович
источник
Его пример показывает, что он хочет, чтобы функция вызывалась, если она определена, а если нет, то ничего не происходит. jsfiddle.net/nh1cxxg6 Функции, которые возвращают значения falsey / false, по-прежнему вызываются. Конечно, это не работает, когда вы хотите, чтобы значения были возвращены.
Самир Алаймович
Ах, я неправильно понял его вопрос. Однако это может вызвать исключение, если обратный вызов не является функцией.
Фрэнк Брайс