假设我有一个变量,其定义如下:
var a = function() {/* Statements */};
我希望有一个函数可以检查变量的类型是否类似于函数。例如:
function foo(v) {if (v is function type?) {/* do something */}};
foo(a);
我如何检查变量 a
是否为上述定义的 Function
类型?
假设我有一个变量,其定义如下:
var a = function() {/* Statements */};
我希望有一个函数可以检查变量的类型是否类似于函数。例如:
function foo(v) {if (v is function type?) {/* do something */}};
foo(a);
我如何检查变量 a
是否为上述定义的 Function
类型?
对于那些对函数式编程感兴趣,或者寻找更具表现力的方法来进行元编程(如类型检查),使用Ramda库完成这样的任务可能很有趣。
下面的代码仅包含纯函数和pointfree函数:
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);
当然,函数应该受到保护以防止null
和undefined
值的出现,以使其“安全”:
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 ));
然而,请注意,由于广泛使用高阶函数,此解决方案可能显示出比其他可用选项更低的性能。
这是一个旧问题,但是在2022年有一些考虑因素:
首先,浏览器兼容性:所有现代浏览器以及Deno和NodeJS都支持instanceof
。此外,它的语法可读性更强,比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),
})
需要理解,这是一个为基准测试优化的函数。当进行基准测试时,您希望通过所有测试,例如 null
、undefined
和一些可能接收的参数。使用 f && ...
过滤这些类似于 null
的参数以减少计算时间。
instanceof
操作符 的注意事项:此操作符在对象的原型链中测试 constructor.prototype
的存在。这通常(但不总是)意味着对象是由构造函数构造的。因此,与 typeof
操作符相比,这个过程较慢。
typeof v === 'function'
typeof
操作符 的注意事项:此操作符返回指示操作数值类型的字符串。执行速度非常快。
instanceof
和typeof
运算符的注意事项:请记住,class declaration在这些操作符中也被视为函数,正如您在此片段中所看到的那样:
// 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%。
_.isFunction(function(){});
// => true
_.isFunction(/abc/);
// => false
_.isFunction(true);
// => false
_.isFunction(null);
// => false
true
表示值为函数,否则返回false
。node.js
测试):var isFunction = function(o) {
return Function.prototype.isPrototypeOf(o);
};
console.log(isFunction(function(){})); // true
console.log(isFunction({})); // false
我发现在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
var util = require('util');
util.isFunction('foo');
在JavaScript中,你应该使用typeOf
运算符。
var a=function(){
alert("fun a");
}
alert(typeof a);// alerts "function"
function isFunction(value) {
return value instanceof Function
}
const toCallback = (v)=>(v instanceof Function)?v:()=>v
let someFunction=(valueOrFunction)=>{
let f = toCallback(valueOrFunction);
[1,2,3].forEach(v=>console.log(`value passed: ${f()}`))
}
现在您可以以两种方式执行上述操作:
someFunction(5);
//value passed: 5
//value passed: 5
//value passed: 5
someFunction(()=>Math.random());
//value passed: 0.3218832537523002
//value passed: 0.2343617971814611
//value passed: 0.38216627030533656
isES3Function
,可以检测传入的value
是否是在ES3中独有的函数类型,而isGenericFunction
则可以检测传入的value
是否是一个通用的(或者说不具体/非具体)函数,因此可以是一个传统的ES3函数或者一个非异步箭头函数。 - undefined