检查变量是否为函数类型

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

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

我想要一个函数来检查变量的类型是否类似函数。即:

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

如何按照上面定义的方式检查变量a是否为类型Function

805687 次浏览
if (typeof v === 'function') {// do something}

Underscore.js使用更精细但高性能的测试:

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

见:https://jsben.ch/B6h73

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

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

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

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

说明:此解决方案不适用于异步函数、生成器或代理函数。有关更多最新解决方案,请参阅其他答案。

你的解决方案有相当多的冗长。如果这样写会更清楚:

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

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

// Test datavar f1 = function () { alert("test"); }var o1 = { Name: "Object_1" };F_est = function () { };var o2 = new F_est();
// Resultsalert(f1 instanceof Function); // truealert(o1 instanceof Function); // falsealert(o2 instanceof Function); // false

我发现在IE8中测试原生浏览器功能时,使用toStringinstanceoftypeof不起作用。这是一个在IE8中运行良好的方法(据我所知):

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

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

"getElementById" in document

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

有几种方法,所以我将总结它们

  1. 最好的方法是:
    function foo(v) {if (v instanceof Function) {/* do something */} };
    
    最高性能(没有字符串比较)和优雅的解决方案-instance of运算符在浏览器中已经支持了很长时间,所以不用担心-它将在IE 6中工作。
  2. 下一个最好的方法是:
    function foo(v) {if (typeof v === "function") {/* do something */} };
    
    typeof的缺点是它很容易出现无声故障,坏的,所以如果你有一个错别字(例如“finction”)-在这种情况下,“if”只会返回false,你不会知道你有一个错误,直到后来在你的代码中
  3. 下一个最好的方法是:
    function isFunction(functionToCheck) {var getType = {};return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';}
    
    这与解决方案#1或#2相比没有优势,但可读性要差得多
    function isFunction(x) {return Object.prototype.toString.call(x) == '[object Function]';}
    
    但仍然比解决方案#1少得多的语义

你应该在js中使用#0运算符

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

jQuery(自3.3版以来已弃用)参考

$.isFunction(functionName);

AngularJS参考

angular.isFunction(value);

Lodash参考

_.isFunction(value);

下划线参考

_.isFunction(object);

Node.js自v4.0.0以来已弃用参考

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

另一种简单的方式:

var fn = function () {}if (fn.constructor === Function) {// true} else {// false}

从节点v0.11开始,您可以使用标准的util函数:

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

下面似乎也适用于我(从node.js开始测试):

var isFunction = function(o) {return Function.prototype.isPrototypeOf(o);};
console.log(isFunction(function(){})); // trueconsole.log(isFunction({})); // false

const foo = function() {};if (typeof foo === 'function') {console.log('is function')}

我想你可以在Function原型上定义一个标志,并检查你要测试的实例是否继承了它

定义一个标志:

Function.prototype.isFunction = true;

然后检查它是否存在

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

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

如果你使用Lodash,你可以用_. isFunction来做。

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

如果value是函数,则此方法返回true,否则返回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开始,async函数可用,因此我们也可以检查它们:

const equalsAsyncFunction = isPrototypeEquals(async () => {});
const isAsyncFunction = R.pipe(Object.getPrototypeOf, equalsAsyncFunction);

然后将它们组合在一起:

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

当然,函数应该受到nullundefined值的保护,因此要使其“安全”:

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

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

具有更多浏览器支持并包含异步函数的内容可能是:

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

然后像这样测试它:

isFunction(isFunction); //trueisFunction(function(){}); //trueisFunction(()=> {}); //trueisFunction(()=> {return 1}); //trueisFunction(async function asyncFunction(){}); //trueisFunction(Array); //trueisFunction(Date); //trueisFunction(Object); //trueisFunction(Number); //trueisFunction(String); //trueisFunction(Symbol); //trueisFunction({}); //falseisFunction([]); //falseisFunction("function"); //falseisFunction(true); //falseisFunction(1); //falseisFunction("Alireza Dezfoolian"); //false

注意这一点:

typeof Object === "function" // true.typeof Array  === "function" // true

如果你正在寻找一个简单的解决方案:

 function isFunction(value) {return value instanceof Function}

这是一个老问题,但在2022年有一些考虑因素:

首先,所有现代浏览器以及Deno和NodeJS都支持浏览器兼容性instanceof。此外,它在语法上可读,比typeof更友好。最后,它比字符串比较提供了良好的性能,但比typeof慢。因此,对我来说,这是一个很好的选择

const fnc = () => {}const isFunction = f => !!f && f instanceof Functionconst isFunctionFaster = f => !!f && 'function' === typeof f
console.log({isFunction: isFunction(fnc),isFunctionFaster: isFunctionFaster(fnc),})

公告

重要的是要理解这是一个用于基准测试的优化函数。当你基准测试时,你想通过所有的测试,比如nullundefined和一些可能收到的参数。f && ...过滤这个null之类的参数以减少计算时间。

此运算符测试对象原型链中constructor.prototype的存在。这通常(但不总是)意味着对象是用构造函数构造的。因此,与typeof运算符相比,此过程较慢。

typeof v==='函数')

此运算符返回一个字符串,指示操作数值的类型。这执行得非常快。

  • instanceoftypeof操作符的注意事项:

请记住,类声明也被这些运算符视为一个函数,正如您在这段代码片段中看到的那样:

// Class Declarationclass A {}
// Instancesconst obj = {}const arr = []const fnc = () => {}const a = new A()
console.log('typeof')console.log(`Object[${typeof Object}], obj[${typeof obj}]`)console.log(`Array[${typeof Array}], arr[${typeof arr}]`)console.log(`Function[${typeof Function}], fnc[${typeof fnc}]`)console.log(`A[${typeof A}], a[${typeof a}]`)
console.log('instanceof')console.log(`Object[${Object instanceof Object}], obj[${obj instanceof Object}]`)console.log(`Array[${Array instanceof Array}], arr[${arr instanceof Array}]`)console.log(`Function[${Function instanceof Function}], fnc[${fnc instanceof Function}]`)console.log(`A[${A instanceof A}], a[${a instanceof A}]`)

以下是不同实例中isFunctionisFunctionFaster用法的基本示例:

// Functionsconst isNil = o => o == nullconst isFunction = f => !!f && f instanceof Functionconst isFunctionFaster = f => !!f && 'function' === typeof f
class A {}
function basicFnc(){}async function asyncFnc(){}
const arrowFnc = ()=> {}const arrowRFnc = ()=> 1
// Not functionsconst obj = {}const arr = []const str = 'function'const bol = trueconst num = 1const a = new A()
const list = [isFunction,isFunctionFaster,basicFnc,arrowFnc,arrowRFnc,asyncFnc,Array,Date,Object,Number,String,Symbol,A,obj,arr,str,bol,num,a,null,undefined,]
for (const arg of list) {console.log(`${arg} is a function: ${isFunction(arg)}`)}

以下是这些函数的基本基准:

/*** Figure out how long it takes for a method to execute.** @param {Function} method to test* @param {number} iterations number of executions.* @param {Array} args to pass in.* @param {T} context the context to call the method in.* @return {number} the time it took, in milliseconds to execute.*/const bench = (method, list, iterations, context) => {let start = 0const timer = action => {const time = performance.now()switch (action) {case 'start':start = timereturn 0case 'stop':const elapsed = time - startstart = 0return elapseddefault:return time - start}};
const result = []timer('start')list = [...list]for (let i = 0; i < iterations; i++) {for (const args of list) {result.push(method.apply(context, args))}}const elapsed = timer('stop')    
console.log(`Called method [${method.name}]`)console.log(`Mean: ${elapsed / iterations}`)console.log(`Exec. time: ${elapsed}`)
return elapsed}
const fnc = () => {}const isFunction = (f) => f && f instanceof Functionconst isFunctionFaster = (f) => f && 'function' === typeof f

class A {}
function basicFnc(){}async function asyncFnc(){}
const arrowFnc = ()=> {}const arrowRFnc = ()=> 1
// Not functionsconst obj = {}const arr = []const str = 'function'const bol = trueconst num = 1const a = new A()
const list = [[isFunction],[basicFnc],[arrowFnc],[arrowRFnc],[asyncFnc],[Array],[Date],[Object],[Number],[String],[Symbol],[A],[obj],[arr],[str],[bol],[num],[a],[null],[undefined],]
const e1 = bench(isFunction, list, 10000)const e2 = bench(isFunctionFaster, list, 10000)
const rate = e2/e1const percent = Math.abs(1 - rate)*100
console.log(`[isFunctionFaster] is ${(percent).toFixed(2)}% ${rate < 1 ? 'faster' : 'slower'} than [isFunction]`)

结论

一般来说,isFunctionFasterisFunction快30%。