关于javascript:检查变量是否是函数类型

Check if a variable is of function type

假设我有任何变量,定义如下:

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

我想要一个函数,它检查变量的类型是否像函数一样。即:

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

如何检查变量a是否为上述定义的Function类型?


1
2
3
if (typeof v ==="function") {
    // do something
}


当然,下划线的方法更有效,但当效率不是问题时,最好的检查方法是写在由@paul rosania链接的下划线页面上。

受下划线启发,最终的is function函数如下:

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


.js使用了一个更复杂但性能更高的测试:

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

参见:http://jspef.com/alternative-isfunction-implements

编辑:更新的测试表明类型可能更快,请参阅http://jspef.com/alternative-isfunction-implements/4


有几种方法,我将对它们进行总结

  • 最好的方法是:
    1
    function foo(v) {if (v instanceof Function) {/* do something */} };

    大多数性能(无字符串比较)和优雅的解决方案——InstanceOfOperator在浏览器中已经支持很长时间了,所以不用担心——它将在IE6中工作。

  • 下一个最好的方法是:
    1
    function foo(v) {if (typeof v ==="function") {/* do something */} };

    typeof的缺点是它容易发生无声故障,很糟糕,所以如果你有错别字(例如"finction")——在这种情况下,"if"只会返回false,直到稍后的代码中你才会知道你有错误。

  • 下一个最好的方法是:
    1
    2
    3
    4
    function isFunction(functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
    }

    与解决方案1或2相比,这没有优势,但可读性要差得多。改进后的版本是

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

    但语义仍然比解决方案低很多1


  • jquery(自3.3版起已弃用)引用

    1
    $.isFunction(functionName);

    AngularJS参考

    1
    angular.isFunction(value);

    矿脉基准

    1
    _.isFunction(value);

    下划线引用

    1
    _.isFunction(object);

    自v4.0.0引用以来不推荐使用node.js

    1
    2
    var util = require('util');
    util.isFunction(object);

    @大复杂:你的解决方案相当冗长。如果这样写的话会更清楚:

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


    1
    2
    3
    4
    var foo = function(){};
    if (typeof foo ==="function") {
      alert("is function")
    }


    尝试使用instanceof运算符:似乎所有函数都继承自Function类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 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

    另一种简单的方法是:

    1
    2
    3
    4
    5
    6
    var fn = function () {}
    if (fn.constructor === Function) {
      // true
    } else {
      // false
    }


    对于那些对函数风格感兴趣,或者希望在元编程中使用更具表现力的方法(比如类型检查)的人来说,看到Ramda库来完成这样的任务可能很有趣。

    下一个代码只包含纯函数和无点函数:

    1
    2
    3
    4
    5
    6
    7
    const R = require('ramda');

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

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

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

    截至ES2017年,async功能可用,因此我们也可以对照这些功能进行检查:

    1
    2
    3
    const equalsAsyncFunction = isPrototypeEquals(async () => {});

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

    然后将它们组合在一起:

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

    当然,应该保护功能不受nullundefined值的影响,使其"安全":

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

    并且,完整的片段总结如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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 ));

    但是,请注意,由于高阶函数的广泛使用,此解决方案的性能可能比其他可用选项低。


    具有更多浏览器支持以及包括异步函数的功能可以是:

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

    然后像这样测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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


    如果您使用lodash,您可以使用.isfunction来完成它。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    _.isFunction(function(){});
    // => true

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

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

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

    此方法返回true,如果值是函数,则返回false


    以下似乎对我也适用(从node.js测试):

    1
    2
    3
    4
    5
    6
    var isFunction = function(o) {
         return Function.prototype.isPrototypeOf(o);
    };

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

    我发现,在IE8中测试本机浏览器功能时,使用toStringinstanceoftypeof都不起作用。这里有一个在IE8中工作良好的方法(据我所知):

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

    或者,可以使用以下命令检查本机函数:

    1
    "getElementById" in document

    不过,我在某个地方读到,在IE7及以下版本中,这并不总是有效。


    由于节点v0.11,您可以使用标准的util函数:

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


    我认为您可以在函数原型上定义一个标志,并检查您想要测试的实例是否继承了这个标志

    定义标志:

    1
    Function.prototype.isFunction = true;

    然后检查它是否存在

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

    缺点是,另一个原型可以定义相同的标志,然后它就毫无价值了,但是如果您对所包含的模块拥有完全的控制权,那么这是最简单的方法


    您应该在JS中使用typeof操作符。

    1
    2
    3
    4
    var a=function(){
        alert("fun a");
    }
    alert(typeof a);// alerts"function"

    如前面的一些答案所示,解决方案是使用typeof。以下是nodejs中的代码片段,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        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]();
      }