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

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

我想要一个函数来检查变量的类型是否为类函数。例如:

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

我怎样才能检查变量a是否为上述定义的函数类型?


当前回答

js使用了一个更精细但高性能的测试:

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

参见:https://jsben.ch/B6h73

编辑:更新的测试表明typeof可能更快,参见https://jsben.ch/B6h73

其他回答

我发现在测试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及以下版本并不总是有效。

我认为你可以在Function原型上定义一个标志,然后检查你想测试的实例是否继承了这个标志

定义一个标志:

Function.prototype.isFunction = true; 

然后检查它是否存在

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

缺点是,另一个原型可以定义相同的标志,然后它是没有价值的,但如果你可以完全控制所包含的模块,这是最简单的方法

当然,下划线的方式更有效率,但当效率不是问题时,最好的检查方法是在@Paul Rosania的下划线页面上写的。

受下划线的启发,最后的isFunction函数如下:

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

注意:此解决方案不适用于异步函数、生成器或代理函数。请参阅其他答案,以了解更多最新的解决方案。

下面的代码似乎也适用于我(从node.js测试):

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

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

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

下面的代码只包含纯函数和无点函数:

const R = require('ramda');

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

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

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

从ES2017开始,异步函数是可用的,所以我们也可以检查它们:

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

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

然后把它们组合在一起:

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

当然,函数应该被保护不受空值和未定义值的影响,所以为了使它“安全”:

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

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