Проверьте, имеет ли переменная тип функции


904

Предположим, у меня есть любая переменная, которая определяется следующим образом:

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

Я хочу функцию, которая проверяет, является ли тип переменной как функция. т.е.

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

Как я могу проверить, имеет ли переменная aтип Functionописанным выше способом?


2
вот эталон для этого наиболее распространенного способа проверить это jsben.ch/#/e5Ogr
EscapeNetscape

Ответы:


380

Конечно, способ подчеркивания более эффективен, но лучший способ проверить, когда эффективность не является проблемой, написан на странице подчеркивания, на которую ссылается @Paul Rosania.

Вдохновленная подчеркиванием, заключительная функция isFunction выглядит следующим образом:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}

1
Может быть кто-то, кто провел более обширное исследование по этому вопросу, но взглянул на результаты 4-й редакции jsperf с простой «проверкой результатов» . isFunctionAпоказывает разницу в реализации по сравнению с другими методами.
Джоэл Пурра

24
С обновленными тестами производительности , похоже , есть огромная разница в скорости в зависимости от вашего браузера. В Chrome, typeof(obj) === 'function'похоже, самый быстрый на сегодняшний день; однако в Firefox obj instanceof Functionявный победитель.
Джастин Варкентин

7
Относительно части: typeof следует использовать только для проверки, не определены ли переменные или свойства. На javascript.info/tutorial/type-detection в разделе «Хорошее использование typeof и следующего» автор заявляет, что дело обстоит с точностью до наоборот
Конрад Мадей,

11
Алекс, мне любопытно, почему ты говоришь, что typeof следует использовать только для проверки неопределенности. Его название предполагает, что его целью является проверка типа чего-либо, а не только существует ли оно. Существуют ли технические проблемы или проблемы, которые могут возникнуть при его использовании для проверки типов, или это больше предпочтение? Если есть предостережения, было бы хорошо перечислить их, чтобы другие могли не споткнуться о них.
Jinglesthula

14
неоправданно сложное решение
Даниэль Гармошка

1715
if (typeof v === "function") {
    // do something
}

44
Просто следите за некоторыми вещами, такими как typeof Object, и typeof Date, и typeof Stringвсе 'function'тоже возвращаются .
Дейв Уорд

359
@ Дэйв, я не уверен, в чем проблема, так как это функции.
Мэтью Крамли

6
В чем разница if (v instanceOf Function)?
Маквандалле

10
@mquandalle Нет большой разницы, за исключением того, instanceofчто не сработает, если вы проверяете функцию, пришедшую из другого документа (возможно, iframe) Производительность варьируется.
rvighne

8
в отличие от принятого ответа, это работает и для асинхронных функций. Для asyncfunctionToCheck, getType.toString.call(functionToCheck)==='[object AsyncFunction]'где, какtypeof asyncfunctionToCheck === 'function'
TDreama

132

Underscore.js использует более сложный, но высокопроизводительный тест:

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

Смотрите: http://jsperf.com/alternative-isfunction-implementations

РЕДАКТИРОВАТЬ: обновленные тесты показывают, что typeof может быть быстрее, см. Http://jsperf.com/alternative-isfunction-implementations/4


Есть ли какая-то особая причина, чтобы использовать этот подход, чем typof?
sv_in

1
Версия Underscore намного быстрее в большинстве браузеров. См .: jsperf.com/alternative-isfunction-implementations
Пол Розания,

13
Я конечно подчеркиваю, просто, но мощно. Тем не менее, вероятно, стоит отметить, что эта реализация может быть подделана объектом с этими атрибутами.
Studgeek

3
@PaulRosania Ранее на сегодняшних тестах производительности они были обновлены и дополнены проверкой, а также добавлены дополнительные тестовые данные в редакции 4 - пожалуйста, запустите их, чтобы увеличить охват тестированием браузера. Это медленнее в операциях в секунду (из-за большого количества тестов), но также показывает одно различие в реализации (откройте консоль javascript и перезагрузите страницу, могут появиться зарегистрированные сообщения об ошибках). Примечание. В Revision 3 добавлена ​​функция isFunctionD (только на основе typeof == "function"), и, похоже, она работает намного быстрее, чем «быстрая» версия Underscore.
Джоэл Пурра

6
Этот ответ немного вводит в заблуждение, поскольку сейчас Underscore.js находится на редакции 12 , а не на упомянутой выше редакции 4 теста альтернативных isFunction()реализаций. В настоящее время он использует что-то очень близкое к тому, что предложил Дандеан .
Джонатан Юнис

113

Есть несколько способов, поэтому я обобщу их все

  1. Лучший способ это:
    function foo (v) {if (v instanceof Function) {/ * сделать что-то * /}};
    
    
    Наиболее производительное (без сравнения строк) и элегантное решение - оператор instanceof поддерживается в браузерах очень долгое время, так что не волнуйтесь - он будет работать в IE 6.
  2. Следующий лучший способ:
    function foo (v) {if (typeof v === "function") {/ * сделать что-то * /}};
    
    
    недостатком typeofявляется то, что он подвержен тихому отказу, что плохо, поэтому, если у вас есть опечатка (например, «finction») - в этом случае `if` просто вернет false, и вы не будете знать, что у вас будет ошибка, пока позже в ваш код
  3. Следующий лучший способ:
    function isFunction (functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call (functionToCheck) === '[Функция объекта]';
    }
    
    
    Это не имеет никакого преимущества перед решением № 1 или № 2, но намного менее читабельно. Улучшенная версия этого
    function isFunction (x) {
        return Object.prototype.toString.call (x) == '[Функция объекта]';
    }
    
    
    но все же намного менее семантически, чем решение № 1

10
Первое решение терпит неудачу в случае передачи функции в другой контекст фрейма. Например, из фрейма top.Function !== Function. Чтобы быть уверенным, используйте второй (любая ошибка написания «функции» будет исправлена ​​во время отладки, мы надеемся).
MaxArt

Что касается вашей точки зрения на опечатки: любой код, имеющий эту опечатку, вероятно, быстро потерпит неудачу / не более очевиден, чем любые другие ошибки, основанные на опечатках. typeof === "функция" является самым чистым решением. Кроме того, ваше «лучшее» решение, которое вы используете instanceof, не учитывает множественные глобальные переменные, такие как проверка по кадрам, и может возвращать ложные отрицания.
brainkim


56

@grandecomplex: в вашем решении довольно много подробностей. Было бы намного понятнее, если бы написано так:

function isFunction(x) {
  return Object.prototype.toString.call(x) == '[object Function]';
}

Object.prototype.toString.call полезен для других типов javascript, которые вас интересуют. Вы можете даже проверить нулевой или неопределенный, что делает его очень мощным.
Джейсон Фолья


35

Попробуйте instanceofоператор: кажется, что все функции наследуются от Functionкласса:

// 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

Что-то с большей поддержкой браузера, а также с асинхронными функциями может быть:

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

а затем проверить это как:

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

Для тех, кто интересуется функциональным стилем или ищет более выразительный подход для использования в метапрограммировании (например, проверку типов), было бы интересно увидеть библиотеку Ramda для выполнения такой задачи.

Следующий код содержит только чистые и точечные функции:

const R = require('ramda');

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

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

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

Начиная с ES2017, asyncфункции доступны, поэтому мы также можем проверить их:

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

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

А затем объединить их вместе:

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

Конечно, функция должна быть защищена от nullи undefinedзначений, чтобы сделать ее «безопасной»:

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

И полный фрагмент, чтобы подвести итог:

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

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


7

Если вы используете Lodash, вы можете сделать это с помощью _.isFunction .

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

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

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

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

Этот метод возвращает, trueесли значение является функцией, иначе false.


4

Я обнаружил , что при тестировании встроенных функций браузера в IE8, используя toString, instanceofи typeofне работой. Вот метод, который отлично работает в IE8 (насколько я знаю):

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

Кроме того, вы можете проверить наличие встроенных функций, используя:

"getElementById" in document

Хотя я где-то читал, что это не всегда будет работать в IE7 и ниже.


4

Ниже, кажется, работает для меня (проверено от node.js):

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

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false

4

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

определить флаг:

Function.prototype.isFunction = true; 

а затем проверьте, существует ли он

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

Недостатком является то, что другой прототип может определять тот же флаг, и тогда он бесполезен, но если у вас есть полный контроль над включенными модулями, это самый простой способ


Сбой с ошибкой, если foo не определено. Не говоря уже о том, что вы модифицируете нативный прототип, что совершенно неправильно.
Камиль Ожеховски

3

Начиная с узла v0.11 вы можете использовать стандартную функцию util:

var util = require('util');
util.isFunction('foo');

2
util.isFunction устарела
kehers

2

Вы должны использовать typeOfоператор в JS.

var a=function(){
    alert("fun a");
}
alert(typeof a);// alerts "function"

0

Решение, как показали некоторые предыдущие ответы, заключается в использовании typeof. ниже приведен фрагмент кода в 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]();
  }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.