假设有任意变量,定义如下:
var a = function() {/* Statements */};
我想要一个函数来检查变量的类型是否为类函数。例如:
function foo(v) {if (v is function type?) {/* do something */}};
foo(a);
我怎样才能检查变量a是否为上述定义的函数类型?
假设有任意变量,定义如下:
var a = function() {/* Statements */};
我想要一个函数来检查变量的类型是否为类函数。例如:
function foo(v) {if (v is function type?) {/* do something */}};
foo(a);
我怎样才能检查变量a是否为上述定义的函数类型?
当前回答
这是一个老问题,但在2022年有一些考虑:
首先,浏览器兼容性:所有现代浏览器以及Deno和NodeJS都支持instanceof。 此外,它在语法上可读,比typeof更友好。 最后,它提供了比字符串比较更好的性能,但比typeof慢。因此,对我来说,这是一个很好的选择
Const FNC = () => {} const isFunction = f => !f && f instanceof函数 const isFunctionFaster = f => !!F && 'function' ===类型的F console.log ({ isFunction: isFunction (fnc), isFunctionFaster: isFunctionFaster (fnc), })
请注意
重要的是要理解这是一个用于基准测试的优化函数。当你做基准测试时,你想要通过所有的测试,比如null, undefined和一些接收到的可能参数。F &&…过滤此null类参数以减少计算时间。
instanceof操作符的注意事项:
此操作符测试构造函数是否存在。对象的原型链中的原型。这通常(虽然不总是)意味着对象是用构造函数构造的。因此,这个过程比typeof操作符慢。
Typeof v === 'function')
typeof操作符的注意事项:
该操作符返回指示操作数值类型的字符串。执行速度非常快。
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}]`)
下面是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)}`) }
下面是这些函数的基本基准:
/** * 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]`)
结论
一般来说,isFunctionFaster比isFunction快30%。
其他回答
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]';
}
注意:此解决方案不适用于异步函数、生成器或代理函数。请参阅其他答案,以了解更多最新的解决方案。
这是一个老问题,但在2022年有一些考虑:
首先,浏览器兼容性:所有现代浏览器以及Deno和NodeJS都支持instanceof。 此外,它在语法上可读,比typeof更友好。 最后,它提供了比字符串比较更好的性能,但比typeof慢。因此,对我来说,这是一个很好的选择
Const FNC = () => {} const isFunction = f => !f && f instanceof函数 const isFunctionFaster = f => !!F && 'function' ===类型的F console.log ({ isFunction: isFunction (fnc), isFunctionFaster: isFunctionFaster (fnc), })
请注意
重要的是要理解这是一个用于基准测试的优化函数。当你做基准测试时,你想要通过所有的测试,比如null, undefined和一些接收到的可能参数。F &&…过滤此null类参数以减少计算时间。
instanceof操作符的注意事项:
此操作符测试构造函数是否存在。对象的原型链中的原型。这通常(虽然不总是)意味着对象是用构造函数构造的。因此,这个过程比typeof操作符慢。
Typeof v === 'function')
typeof操作符的注意事项:
该操作符返回指示操作数值类型的字符串。执行速度非常快。
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}]`)
下面是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)}`) }
下面是这些函数的基本基准:
/** * 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]`)
结论
一般来说,isFunctionFaster比isFunction快30%。
从节点v0.11开始,你可以使用标准的util函数:
var util = require('util');
util.isFunction('foo');
对于那些对函数式风格感兴趣,或者在元编程中寻找更有表现力的方法(如类型检查)的人来说,看到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 ));
但是,请注意,由于大量使用高阶函数,此解决方案的性能可能低于其他可用选项。