You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:35:12