IE8环境下如何使用Function.prototype.call替代apply实现参数透传
实现方案
你遇到的核心问题是call需要逐个传入参数,而你之前的写法把整个参数数组作为单个参数传入,才会输出数组格式。我们可以通过动态匹配参数个数的方式,用call完全模拟apply的效果,兼容IE8环境。
通用工具函数实现
我们可以封装一个通用的转换函数,所有需要用到apply的场景都可以用该函数替代:
/** * 用call模拟apply的兼容工具函数 * @param {Function} fn 要执行的原函数 * @param {any} ctx 函数执行的this指向 * @param {Array | Arguments} args 参数集合,支持类数组对象 */ function applyWithCall(fn, ctx, args) { args = args || []; // 先处理常见参数长度的场景,性能最优 switch(args.length) { case 0: return fn.call(ctx); case 1: return fn.call(ctx, args[0]); case 2: return fn.call(ctx, args[0], args[1]); case 3: return fn.call(ctx, args[0], args[1], args[2]); case 4: return fn.call(ctx, args[0], args[1], args[2], args[3]); case 5: return fn.call(ctx, args[0], args[1], args[2], args[3], args[4]); // 可根据实际业务需要继续扩展参数长度,一般到10就足够覆盖绝大多数场景 default: // 超长参数场景,动态构造调用逻辑,IE8原生支持new Function语法 var paramList = []; for(var i = 0; i < args.length; i++) { paramList.push('args[' + i + ']'); } return new Function('fn', 'ctx', 'args', 'return fn.call(ctx, ' + paramList.join(',') + ')')(fn, ctx, args); } }
业务代码改造示例
对应你给出的testLog改造后写法如下:
function testLog(){ applyWithCall(console.log, console, arguments); } testLog(1,2,3) // 输出1 2 3,和原apply实现效果完全一致
原理解释
- 短参数场景直接走switch分支,没有任何额外性能损耗,完全符合IE8的语法支持范围
- 长参数场景通过动态拼接参数列表的方式,间接实现了参数展开的效果,不需要依赖ES6的展开运算符
- 该工具函数是通用实现,不绑定
console.log场景,所有需要使用apply的逻辑都可以直接替换使用
内容的提问来源于stack exchange,提问作者nowjsio
相关产品推荐
相关产品推荐

