检查变量是否为函数类型

Jes*_* Vn 826 javascript

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

var a = function() {/* Statements */};
Run Code Online (Sandbox Code Playgroud)

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

function foo(v) {if (v is function type?) {/* do something */}};
foo(a);
Run Code Online (Sandbox Code Playgroud)

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

sel*_*bie 1600

if (typeof v === "function") {
    // do something
}
Run Code Online (Sandbox Code Playgroud)

  • @Dave,我不确定问题是什么,因为那些是函数. (325认同)
  • 只要注意一些东西,比如`typeof Object`,`typeof Date`和`typeof String`,它们都会返回''function'. (39认同)
  • @mquandalle没有什么大的区别,除非你正在检查来自另一个文档的函数(可能是iframe),`instanceof`将不起作用.表现各异. (9认同)
  • 与接受的答案不同,这也适用于异步功能.对于`asyncfunctionToCheck`,`getType.toString.call(functionToCheck)==='[object AsyncFunction]'`其中`typeof asyncfunctionToCheck ==='function' (5认同)
  • 和`if(v instanceOf Function)`有什么区别? (4认同)
  • 我不明白为什么这不是选择的答案,因为类型函数的每个对象都只需使用JS的“ typeof”功能即可正确返回此类型。 (3认同)
  • @ebk - 这就是我最初的想法。但是,JavaScript 类型强制可能很危险,因此“===”是最佳实践。 (3认同)
  • @MatthewCrumley - 我知道这是几年前的事了,但很明显,存在需要在“函数”和“日期”之间进行语义区分的用例,我确信这就是戴夫·沃德所指的。 (3认同)
  • @Melab - 只有当你主动知道它们也是函数时 - 从表面上看这并不明显。我只能重复一遍,Dave Ward 的观察对于那些可能不了解 JS 内部实现特性的人来说具有重要价值。我将进一步指出,“按名称区分”本质上是一种容易出错的方法,因为它需要对当前和未来的“要排除的所有语言名称”有全面的了解。 (3认同)

Ale*_*nde 347

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

灵感来自下划线,最终的isFunction功能如下:

function isFunction(functionToCheck) {
 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
}
Run Code Online (Sandbox Code Playgroud)

  • 使用[更新的性能测试](http://jsperf.com/alternative-isfunction-implementations/9),根据您的浏览器,它看起来速度差异很大.在Chrome`typeof(obj)==='function'`似乎是迄今为止最快的; 然而,在Firefox`obj instanceof Function`是明显的赢家. (18认同)
  • 亚历克斯,我很好奇为什么你说typeof应该只是用来检查undefined.它的名字暗示它的目的是检查某些东西的类型,而不仅仅是它是否存在.使用它进行类型检查时是否存在技术问题或问题,还是更偏好?如果有警告,最好列出它们,以便其他人可以避免绊倒它们. (11认同)
  • 不合理的过于复杂的解决方案 (9认同)
  • 请继续滚动以找到更简单的解决方案。 (8认同)
  • 关于部分:*typeof应仅用于检查变量或属性是否未定义.*在http://javascript.info/tutorial/type-detection中*很好地使用typeof*部分和以下作者说,情况正好相反 (7认同)

Pau*_*nia 129

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

_.isFunction = function(obj) {
  return !!(obj && obj.constructor && obj.call && obj.apply);
};
Run Code Online (Sandbox Code Playgroud)

请参阅:http://jsperf.com/alternative-isfunction-implementations

编辑:更新的测试表明typeof可能更快,请参阅http://jsperf.com/alternative-isfunction-implementations/4

  • 我确实喜欢下划线,简单但功能强大.也就是说,可能值得注意的是,这个实现可能被具有这些属性的对象欺骗. (13认同)
  • 这个答案现在有点误导,因为Underscore.js现在正在[修订版12](http://jsperf.com/alternative-isfunction-implementations/12),而不是上面提到的修订版4,它的替代测试`isFunction()`实现.它目前使用的东西非常接近[dandean](http://stackoverflow.com/users/374773/dandean)的建议. (6认同)
  • @PaulRosania今天早些时候偶然发现了这些性能测试,并[通过验证更新它们以及更多测试数据作为修订版4](http://jsperf.com/alternative-isfunction-implementations/4) - 请运行它以增加浏览器测试覆盖率.它的每秒操作速度较慢(由于许多测试),但它也显示了一个实现差异(打开你的javascript控制台和重新加载页面,可能会记录错误消息).注意:修订版3添加了isFunctionD(仅基于`typeof =="函数"`) - 它似乎比Underscore的"快速"版本**快得多**. (3认同)
  • Underscore.js 现在使用 `typeof obj == 'function' || 假`。([来源](https://github.com/jashkenas/underscore/blob/943977e34e2279503528a71ddcc2dd5f96483945/modules/isFunction.js#L11)) (2认同)

小智 103

有几种方法可以总结它们

  1. 最好的方法是:
    
    function foo(v) {if (v instanceof Function) {/* do something */} };
    
    
    最常见的(没有字符串比较)和优雅的解决方案 - 在浏览器中支持instanceof运算符很长一段时间,所以不要担心 - 它将在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少得多的语义

  • 在函数传递给不同的帧上下文的情况下,第一个解决方案失败.例如,来自iframe`top.Function!== Function`.可以肯定的是,使用第二个(在调试期间,任何拼写错误的"函数"都会被纠正,希望如此). (10认同)
  • 关于您关于打字错误的观点:任何有此打字错误的代码都可能很快失败/并不比任何其他基于打字错误的错误更明显。typeof === "function" 是最干净的解决方案。此外,您使用 `instanceof` 的“最佳”解决方案不会考虑多个全局变量,例如跨帧检查,并且可能返回假阴性。 (3认同)

Ces*_*min 81

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

$.isFunction(functionName);
Run Code Online (Sandbox Code Playgroud)

AngularJS 参考

angular.isFunction(value);
Run Code Online (Sandbox Code Playgroud)

Lodash 参考

_.isFunction(value);
Run Code Online (Sandbox Code Playgroud)

下划线 参考

_.isFunction(object); 
Run Code Online (Sandbox Code Playgroud)

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

var util = require('util');
util.isFunction(object);
Run Code Online (Sandbox Code Playgroud)


dan*_*ean 54

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

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


小智 47

var foo = function(){};
if (typeof foo === "function") {
  alert("is function")
}
Run Code Online (Sandbox Code Playgroud)


小智 31

尝试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
Run Code Online (Sandbox Code Playgroud)


小智 11

另一个简单的方法:

var fn = function () {}
if (fn.constructor === Function) {
  // true
} else {
  // false
}
Run Code Online (Sandbox Code Playgroud)

  • 如果 fn 未定义,则会抛出错误 (2认同)

Ali*_*eza 11

具有更多浏览器支持并且还包括异步功能的功能可能是:

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

然后像这样测试它:

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
Run Code Online (Sandbox Code Playgroud)


Dam*_*nic 8

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

下一个代码只包含pure和pointfree函数:

const R = require('ramda');

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

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

const isSyncFunction = R.pipe(Object.getPrototypeOf, equalsSyncFunction);
Run Code Online (Sandbox Code Playgroud)

从ES2017开始,async功能可用,因此我们也可以检查它们:

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

const isAsyncFunction = R.pipe(Object.getPrototypeOf, equalsAsyncFunction);
Run Code Online (Sandbox Code Playgroud)

然后将它们组合在一起:

const isFunction = R.either(isSyncFunction, isAsyncFunction);
Run Code Online (Sandbox Code Playgroud)

当然,应该保护功能nullundefined价值,以使其"安全":

const safeIsFunction = R.unless(R.isNil, isFunction);
Run Code Online (Sandbox Code Playgroud)

并且,完整的摘要总结:

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 ));
Run Code Online (Sandbox Code Playgroud)

但请注意,由于大量使用高阶函数,此解决方案可能会显示出比其他可用选项更低的性能.


Teo*_*cci 7

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

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

const fnc = () => {}
const isFunction = f => !!f && f instanceof Function
const isFunctionFaster = f => !!f && 'function' === typeof f

console.log({
  isFunction: isFunction(fnc),
  isFunctionFaster: isFunctionFaster(fnc),
})
Run Code Online (Sandbox Code Playgroud)

注意

重要的是要了解这是针对基准测试的优化函数。当您进行基准测试时,您希望通过所有测试,例如nullundefined以及一些关于收到的可能参数的测试。f && ...过滤null类似参数以减少计算时间。

constructor.prototype该运算符测试对象原型链中是否存在。这通常(尽管并非总是)意味着对象是用构造函数构造的。因此,这个过程比typeof操作员要慢。

typeof v === '函数')

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

  • instanceof和操作员的注意事项typeof

请记住,类声明也被这些运算符视为函数,如以下代码片段所示:

// Class Declaration
class A {}

// Instances
const 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}]`)
Run Code Online (Sandbox Code Playgroud)

以下是不同实例isFunction的基本示例:isFunctionFaster

// Functions
const isNil = o => o == null
const isFunction = f => !!f && f instanceof Function
const isFunctionFaster = f => !!f && 'function' === typeof f

class A {}

function basicFnc(){}
async function asyncFnc(){}

const arrowFnc = ()=> {}
const arrowRFnc = ()=> 1

// Not functions
const obj = {}
const arr = []
const str = 'function'
const bol = true
const num = 1
const 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)}`)
}
Run Code Online (Sandbox Code Playgroud)

以下是这些功能的基本基准:

/**
 * 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 = 0
    const timer = action => {
        const time = performance.now()
        switch (action) {
            case 'start':
                start = time
                return 0
            case 'stop':
                const elapsed = time - start
                start = 0
                return elapsed
            default:
                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 Function
const isFunctionFaster = (f) => f && 'function' === typeof f


class A {}

function basicFnc(){}
async function asyncFnc(){}

const arrowFnc = ()=> {}
const arrowRFnc = ()=> 1

// Not functions
const obj = {}
const arr = []
const str = 'function'
const bol = true
const num = 1
const 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/e1
const percent = Math.abs(1 - rate)*100

console.log(`[isFunctionFaster] is ${(percent).toFixed(2)}% ${rate < 1 ? 'faster' : 'slower'} than [isFunction]`)
Run Code Online (Sandbox Code Playgroud)

结论

一般来说isFunctionFaster快于isFunction30%。


slo*_*nzo 6

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

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

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

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

_.isFunction(null);
// => false
Run Code Online (Sandbox Code Playgroud)

true如果value是函数,则此方法返回,否则false.


Mar*_*tus 5

下面的内容似乎也适合我(经过测试node.js):

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

console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false
Run Code Online (Sandbox Code Playgroud)