某些源码中会有这个Function.prototype.apply.call方法,刚开始看特别不理解,为何要写的这么绕~看起来头好疼...仔细研究发现这个方法~很有意思
回忆下apply,call的基本用法,其目的是改变调用方法中的this指向,将其指向为传入的对象
下面来分析下Sentry源码中的一段用到Function.prototype.apply.call方法的代码:
/** JSDoc */
function instrumentConsole(): void {
if (!('console' in global)) {
return;
}
['debug', 'info', 'warn', 'error', 'log', 'assert'].forEach(function(level: string): void {
if (!(level in global.console)) {
return;
}
fill(global.console, level, function(originalConsoleLevel: () => any): Function {
return function(...args: any[]): void {
triggerHandlers('console', { args, level });
// this fails for some browsers. :(
if (originalConsoleLevel) {
Function.prototype.apply.call(originalConsoleLevel, global.console, args);
}
};
});
});
}
先分析下该如何理解
Function.prototype.apply.call(originalConsoleLevel, global.console, args);
首先可以将Function.prototype.apply看成一个整体-->FunctionApply
FunctionApply.call(originalConsoleLevel, global.console, args);
那么将此句翻译一下
originalConsoleLevel.FunctionApply(global.console, args);
然后再翻译一下就是一个普通方法调用了
global.console.originalConsoleLevel(args);

可能很多人根我一样都有一个疑问,为何不直接使用
apply方法,如下所示:originalConsoleLevel.apply(global.console, args),看到react源码中给到的解释是在IE9中有bug。
function printWarning(level, format, args) {
// When changing this logic, you might want to also
// update consoleWithStackDev.www.js as well.
if (__DEV__) {
const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
const stack = ReactDebugCurrentFrame.getStackAddendum();
if (stack !== '') {
format += '%s';
args = args.concat([stack]);
}
const argsWithFormat = args.map(item => '' + item);
// Careful: RN currently depends on this prefix
argsWithFormat.unshift('Warning: ' + format);
// We intentionally don't use spread (or .apply) directly because it
// breaks IE9: https://github.com/facebook/react/issues/13610
// eslint-disable-next-line react-internal/no-production-logging
Function.prototype.apply.call(console[level], console, argsWithFormat);
}
}
欢迎探讨~