es-toolkit isFunction 完全指南:JavaScript 函数类型检测的原理与实战
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
isFunction是 es-toolkit 在predicate分类下提供的一个类型检测工具函数,用于判断给定值是否为函数。它覆盖普通函数、异步函数、生成器函数、构造函数以及各类 JavaScript 内置函数与类,在回调函数验证、动态函数调用等场景中扮演基础而关键的角色。阅读本文后,你将掌握isFunction的完整用法、TypeScript 类型收窄机制,以及它在 es-toolkit 内部被其他工具函数复用的底层原理。
函数签名与核心实现
isFunction的调用形式非常简单:
const result = isFunction(value);其完整源码位于 src/predicate/isFunction.ts,整个实现只有一行,却蕴含着 TypeScript 类型收窄的关键设计:
export function isFunction(value: any): value is (...args: any[]) => any { return typeof value === 'function'; }从源码可以提炼出三个核心事实:
- 判定依据是
typeof运算符:JavaScript 中typeof对函数类型值返回字符串'function',这是语言层面的内置检测机制,任何函数(无论普通、异步、生成器还是构造函数)都会被命中; - 返回类型是类型谓词(type predicate):返回值声明为
value is (...args: any[]) => any,意味着在 TypeScript 中一旦isFunction(value)返回true,编译器就会把value的类型自动收窄为可调用函数,无需额外类型断言; - 参数类型为
any:它接受任意值,因此可以安全地用于检测来自外部输入、回调参数等不可信来源的值。
es-toolkit 还提供了与之等价的 lodash 兼容版本 src/compat/predicate/isFunction.ts,实现完全一致,并通过 src/browser.ts 在浏览器构建入口中导出,方便在浏览器环境下直接使用。
使用方法:检测所有类型的函数
isFunction的最大特点是覆盖面广,可以检测所有类型的函数。在 src/predicate/isFunction.spec.ts 的测试用例中,覆盖了普通函数、异步函数、生成器函数与Proxy构造函数等典型场景。
普通函数
import { isFunction } from 'es-toolkit/predicate'; // 普通函数 console.log(isFunction(function () {})); // true console.log(isFunction(() => {})); // true console.log(isFunction(Array.prototype.slice)); // true其中Array.prototype.slice是方法引用(method reference),在 JavaScript 中方法本身也是函数,因此同样返回true。
异步函数与生成器函数
// 异步函数 console.log(isFunction(async function () {})); // true console.log(isFunction(async () => {})); // true // 生成器函数 console.log(isFunction(function* () {})); // trueasync函数与function*生成器虽然调用方式特殊,但它们的typeof结果依然是'function',因此会被正确识别。测试代码 src/predicate/isFunction.spec.ts 明确验证了这两类函数:
it('should return `true` for async functions', () => { const asyncFunc = async function () {}; expect(isFunction(asyncFunc)).toBe(typeof asyncFunc === 'function'); }); it('should return `true` for generator functions', () => { const genFunc = function* () {}; expect(isFunction(genFunc)).toBe(typeof genFunc === 'function'); });构造函数
// 构造函数 console.log(isFunction(Array)); // true console.log(isFunction(Date)); // true console.log(isFunction(RegExp)); // true console.log(isFunction(Promise)); // trueArray、Date、RegExp、Promise这些内置构造函数本质都是函数对象(需要配合new调用),因此全部返回true。
检测内置 JavaScript 函数与类
isFunction同样适用于 JavaScript 内置的各类构造函数、类型化数组构造器以及 Proxy/Reflect 相关对象:
// 内置构造函数 console.log(isFunction(Object)); // true console.log(isFunction(String)); // true console.log(isFunction(Number)); // true console.log(isFunction(Boolean)); // true // 类型化数组构造函数 console.log(isFunction(Int8Array)); // true console.log(isFunction(Uint8Array)); // true console.log(isFunction(Float32Array)); // true // Proxy 与 Reflect console.log(isFunction(Proxy)); // true console.log(isFunction(Reflect.get)); // true值得注意的是类型化数组构造器这一边界情况。在 src/predicate/isFunction.spec.ts 中,测试对Int8Array、Uint8Array、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array、Float64Array以及DataView等全部类型化数组构造器逐一断言,其期望值与Object.prototype.toString.call(type) === '[object Function]'的结果逐项对齐。这表明isFunction的结果与标准标签检测(tag check)完全一致,行为可预期。
与"看起来像函数"的值区分开
isFunction只认typeof === 'function',因此对非函数值一律返回false:
// 非函数值 console.log(isFunction({})); // false console.log(isFunction([])); // false console.log(isFunction('text')); // false console.log(isFunction(42)); // false console.log(isFunction(null)); // false console.log(isFunction(undefined)); // false // 看起来像函数但不是函数 console.log(isFunction({ call: function () {} })); // false最后一例尤其值得注意:{ call: function () {} }拥有名为call的方法,外形上"可调用",但它是一个普通对象而非函数,typeof结果为'object',因此被正确判定为false。这正体现了isFunction严格基于语言级typeof语义、不做任何"长得像就算"的宽松推断。
在 src/predicate/isFunction.spec.ts 中,测试还利用falsey与args两个内部测试辅助(位于 src/compat/_internal 目录)对false、0、''、null、undefined、NaN以及类数组arguments对象进行了批量验证,全部返回false;对象、数组、日期、错误、正则、字符串、数字、Symbol 等类型也逐一覆盖。
实战场景:回调验证与动态函数调用
场景一:回调函数的类型安全验证
在处理可选回调参数时,isFunction结合 TypeScript 类型谓词,可以消除"回调可能不是函数"的不确定性:
function processData(data: any[], callback?: unknown) { const result = data.map(item => item * 2); if (isFunction(callback)) { // 这里 callback 已被收窄为函数类型,可以安全调用 callback(result); } return result; }由于isFunction的返回类型是value is (...args: any[]) => any,if分支内 TypeScript 编译器会自动将callback视为可调用函数,无需手动断言。
场景二:动态函数执行
当从配置、插件系统或用户输入中拿到一个"可能是函数"的值时,可以用它做动态分发:
function executeIfFunction(fn: unknown, ...args: any[]) { if (isFunction(fn)) { return fn(...args); } console.log('给定值不是函数'); return null; }场景三:方法链中的函数确认
在链式 API 设计中,先用isFunction校验参数再执行映射,可以避免传入非法值时抛出难以定位的错误:
const utils = { data: [1, 2, 3], process(fn: unknown) { if (isFunction(fn)) { this.data = this.data.map(fn); } return this; }, };参数与返回值
| 项目 | 说明 |
|---|---|
参数value | 类型unknown,待检测是否为函数的任意值 |
| 返回值 | 类型value is (...args: any[]) => any,值为函数时返回true,否则返回false |
类型谓词签名意味着返回值同时承担"布尔判断"与"类型收窄"双重职责,这是 es-toolkit 谓词系列(如isString、isNumber、isPlainObject等)共有的设计模式,统一通过 src/predicate/index.ts 对外导出。
在 es-toolkit 内部的实际复用
isFunction不只是独立工具,它还被 es-toolkit 内部多个高阶函数复用,这从侧面印证了它的可靠性与基础地位。在 lodash 兼容层 src/compat 中:
- invokeMap.ts:当
path本身是函数时,isFunction用于判断是直接调用还是走属性路径解析; - wrap.ts:用它决定
wrapper参数是否为函数,否则回退为恒等函数(identity); - cond.ts:在条件函数列表中校验每个元素是否为函数;
- bindAll.ts:过滤出对象上可绑定的函数属性;
- functionsIn.ts:遍历对象属性时用
isFunction筛选函数型属性; - transform.ts:判断对象构造器是否有效。
此外,memoize.spec.ts 等测试也会用isFunction来断言记忆化函数仍保持函数身份。这些内部使用点说明:isFunction是构建bindAll、wrap、cond、invokeMap等复合工具时的公共地基,其单行实现以typeof这一语言原语保证了极低的运行时开销——这也是 es-toolkit 追求"小而快"性能目标的一个缩影。
小结
isFunction用一行typeof value === 'function'实现了对所有函数类型的完备检测,并通过类型谓词让 TypeScript 开发者在运行时校验的同时获得编译期类型收窄。无论是处理可选回调、动态调用、方法链设计,还是理解 es-toolkit 内部wrap、cond、bindAll等函数的实现,它都是值得深入掌握的入门级但高频复用的工具函数。安装 es-toolkit 后,即可通过import { isFunction } from 'es-toolkit/predicate'直接使用。
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考