重写Function.prototype.call触发栈溢出错误的原因探究
重写Function.prototype.call时栈溢出的原因分析
问题现象
尝试重写Function.prototype.call时,代码如下:
Function.prototype.call = function(point,...arg){ console.log('ok'); const key = Symbol('key') point[key] = this const data = point[key](...arg) delete point[key] return data } function fn(a,b){ this.sum = a+b return a + b } const obj = {} const data = fn.call(obj,3,4) console.log(data); console.log(obj);
运行后触发RangeError: Maximum call stack size exceeded,错误栈如下:
node:internal/bootstrap/switches/is_main_thread:149 if (stdout) return stdout; ^ RangeError: Maximum call stack size exceeded at process.getStdout [as stdout] (node:internal/bootstrap/switches/is_main_thread:149:3) at console.get (node:internal/console/constructor:214:42) at console.value (node:internal/console/constructor:340:50) at console.log (node:internal/console/constructor:379:61) at Function.call (C:\Users\xyk\Desktop\my study\nodejs\node.js中级\23重写call函数.js:2:13) at new WriteStream (node:tty:96:14) at createWritableStdioStream (node:internal/bootstrap/switches/is_main_thread:53:16) at process.getStdout [as stdout] (node:internal/bootstrap/switches/is_main_thread:150:12) at console.get (node:internal/console/constructor:214:42) at console.value (node:internal/console/constructor:340:50) Node.js v20.12.1
但将方法名改为Mycall后,逻辑完全相同的代码能正常运行:
Function.prototype.Mycall = function(point,...arg){ console.log('ok'); const key = Symbol('key') point[key] = this const data = point[key](...arg) delete point[key] return data } function fn(a,b){ this.sum = a+b return a + b } const obj = {} const data = fn.Mycall(obj,3,4) console.log(data); console.log(obj);
输出结果:
ok 7 { sum: 7 }
另外发现:删除重写call方法内的console.log()语句后,错误消失;但重写Mycall时保留console.log()也不会报错。
原因解析
覆盖原生call导致递归循环:Node.js的核心模块(比如
console、tty)底层实现严重依赖原生Function.prototype.call。当你覆盖这个方法后,在自定义call里调用console.log时,console.log的内部逻辑会触发调用call方法,而此时call已经被替换成你的自定义版本,于是形成无限递归调用,最终耗尽调用栈导致溢出。删除console.log避免递归触发:去掉
console.log后,自定义call的执行流程不会触发Node.js内部模块对call的依赖,仅处理fn.call(obj,3,4)这一次调用,没有递归循环,因此能正常执行。新增Mycall不影响原生逻辑:
Mycall是新增的原型方法,没有覆盖原生call,Node.js内部代码依然使用原生call方法,不会进入你的自定义逻辑,自然不会出现递归问题。
内容的提问来源于stack exchange,提问作者user25428597
相关产品推荐
相关产品推荐

