Перевірте, чи є змінна функціонального типу


903

Припустимо, у мене є будь-яка змінна, яка визначається наступним чином:

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.

Натхненний підкресленням, остаточна функція функціонування полягає в наступному:

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 в розділі «Добре використання типуofof» та наступному авторі заявляється, що справа якраз навпаки
Konrad Madej

11
Алекс, мені цікаво, чому ти кажеш, що typeof слід використовувати лише для перевірки на невизначеність. Це ім'я говорить про те, що метою є перевірка типу чогось, а не лише того, чи існує воно. Чи є технічні проблеми або проблеми, які можуть виникнути при його використанні для перевірки типу, чи це більше перевагу? Якщо є застереження, було б добре перерахувати їх, щоб інші могли уникнути натрапляння на них.
jinglesthula

14
необґрунтовано надскладне рішення
Даніель Гармошка

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

44
Просто слідкуйте за кілька речей , як typeof Object, typeof Dateі typeof String, що все повертається 'function'теж.
Дейв Уорд

358
@Dave, я не впевнений, у чому проблема, оскільки це функції.
Меттью Крамлі

6
У чому різниця if (v instanceOf Function)?
mquandalle

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

EDIT: оновлені тести дозволяють припустити, що typeof може бути швидшим, див. Http://jsperf.com/alternative-isfunction-implementations/4


Чи є якась конкретна причина для використання цього підходу, ніж typof?
sv_in

1
Версія підкреслення набагато швидша у більшості браузерів. Дивіться: jsperf.com/alternative-isfunction-implementations
Пол Розанія

13
Я впевнений, мені подобається підкреслення, просте, але потужне Це, мабуть, варто зазначити, що ця реалізація може бути підроблена об'єктом із цими атрибутами.
studgeek

3
@PaulRosania Натрапив на ці тести на ефективність раніше сьогодні та оновив їх перевірку плюс додаткові тестові дані, як редакція 4 - запустіть її, щоб збільшити охоплення тестом браузера. Це повільніше в операціях за секунду (через багато тестів), але він також показує одну різницю в реалізації (відкрийте консоль javascript і перезавантажте сторінку; там можуть бути повідомлення про помилки). Примітка: До версії 3 додано isFunctionD (базується лише на typeof == "function") - і, здається, це набагато швидше, ніж "швидка" версія Underscore.
Джоел Пурра

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

112

Існує кілька способів, тому я підсумую їх усі

  1. Найкращий спосіб:
    function foo (v) {if (v instanceof Функція) {/ * робити щось * /}};
    
    
    Найбільш ефективне (без порівняння рядків) та елегантне рішення - оператор instanceof підтримується в браузерах дуже давно, тому не хвилюйтеся - він працюватиме в IE 6.
  2. Наступний найкращий спосіб:
    function foo (v) {if (typeof v === "function") {/ * робити щось * /}};
    
    
    Недоліком typeofє те, що він чутливий до мовчазного відмови, погано, тому якщо у вас помилка друку (наприклад, "фініш") - у цьому випадку `if` просто поверне помилкове значення, і ви не будете знати, що маєте помилку до цього пізніше свій код
  3. Наступний найкращий спосіб:
    функція isFunction (functionToCheck) {
        var getType = {};
        функція поверненняToCheck && getType.toString.call (functionToCheck) === '[функція об'єкта]';
    }
    
    
    Це не має переваги перед рішенням №1 або №2, але набагато менш читабельне. Вдосконалена версія цього є
    функція isFunction (x) {
        повернути Object.prototype.toString.call (x) == '[Функція об'єкта]';
    }
    
    
    але все ж набагато менш смисловий, ніж рішення №1

10
Перше рішення виходить з ладу у випадку, якщо функція передається в інший контекст кадру. Наприклад, з iframe 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, які вас цікавлять. Ви навіть можете перевірити наявність нуля або невизначеності, що робить його дуже потужним.
Джейсон Фолья

49

var foo = function(){};
if (typeof foo === "function") {
  alert("is function")
}


1
... і (() => (typeof obj === 'function') && doSomething())все ще найшвидший варіант.
дарем

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 і перевірити, чи спадкував це екземпляр, який ви хочете перевірити

визначте прапор:

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.