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

如何获取iframe中eval内console.log的执行代码行号?

解决iframe中eval执行代码时console.log无法获取真实行号的问题

我通过将JavaScript代码传入iframe并在eval中执行,来规避引入React、ReactDOM等库时的代码量限制。已经重定义了内置的console.log方法用于向父窗口发送消息,但无法获取该console.log调用对应的真实代码行号——使用new Error().stack只能拿到eval所在的行号,而非用户代码里的实际行号。

现有iframe内脚本代码:

<script>
      const _log = console.log;

      console.log = function (...args) {
        // Send to parent
        window.parent.postMessage(
          {
            id: '${id}',
            source: 'code-preview',
            message: args,
          },
          '*',
        );

        _log.apply(console, args);
      };

      window.addEventListener('message', (event) => {
        const handleError = (error) => {
          const root = document.getElementById('root');
          root.innerHTML = '<div style="color: red;">' + error + '</div>';
          console.error(error);
        };

        window.addEventListener('error', (event) => {
          event.preventDefault();
          handleError(event.error)
        });

        try {
          eval(event.data)
        } catch(error){
          if(error instanceof Error){
            return {
              code: '',
              error: error.message
            }
          } else {
            throw error;
          }
        }
      }, false);
    </script>

可行解决方案

方案1:解析Error栈信息结合用户代码行号映射

修改console.log的重写逻辑,通过生成Error对象解析栈帧,过滤掉无关信息后提取用户代码对应的行号:

const _log = console.log;
// 存储用户传入的原始代码,用于后续行号校验
let userCode = '';

console.log = function (...args) {
  const err = new Error();
  // 拆分栈信息,过滤掉当前console.log和eval相关的栈帧
  const stackFrames = err.stack.split('\n').filter(frame => 
    !frame.includes('console.log') && !frame.includes('eval')
  );

  if (stackFrames.length > 0) {
    // 提取行号(以Chrome栈帧格式为例,如"at <anonymous>:3:13")
    const lineMatch = stackFrames[0].match(/:(\d+):(\d+)/);
    if (lineMatch) {
      // eval中行号直接对应用户代码的行号
      const lineNumber = parseInt(lineMatch[1], 10);
      
      // 向父窗口发送消息时携带行号
      window.parent.postMessage(
        {
          id: '${id}',
          source: 'code-preview',
          message: args,
          line: lineNumber
        },
        '*'
      );
    }
  }

  _log.apply(console, args);
};

window.addEventListener('message', (event) => {
  const handleError = (error) => {
    const root = document.getElementById('root');
    root.innerHTML = '<div style="color: red;">' + error + '</div>';
    console.error(error);
  };

  window.addEventListener('error', (event) => {
    event.preventDefault();
    handleError(event.error)
  });

  try {
    userCode = event.data;
    eval(event.data);
  } catch(error){
    if(error instanceof Error){
      return {
        code: '',
        error: error.message
      }
    } else {
      throw error;
    }
  }
}, false);

方案2:用Function替代eval并指定自定义文件名

使用new Function()替代eval,通过构造函数最后一个参数指定自定义文件名,这样栈信息会明确标识该文件名,行号直接对应用户代码的真实行号,可靠性更高:

const _log = console.log;

console.log = function (...args) {
  const err = new Error();
  const stackFrames = err.stack.split('\n');
  
  // 查找自定义文件名对应的栈帧(这里指定为"user-code.js")
  const userFrame = stackFrames.find(frame => frame.includes('user-code.js'));
  if (userFrame) {
    const lineMatch = userFrame.match(/user-code.js:(\d+):(\d+)/);
    if (lineMatch) {
      const lineNumber = parseInt(lineMatch[1], 10);
      
      window.parent.postMessage(
        {
          id: '${id}',
          source: 'code-preview',
          message: args,
          line: lineNumber
        },
        '*'
      );
    }
  }

  _log.apply(console, args);
};

window.addEventListener('message', (event) => {
  const handleError = (error) => {
    const root = document.getElementById('root');
    root.innerHTML = '<div style="color: red;">' + error + '</div>';
    console.error(error);
  };

  window.addEventListener('error', (event) => {
    event.preventDefault();
    handleError(event.error)
  });

  try {
    // 用Function替代eval,最后一个参数传入自定义文件名
    const userFunction = new Function(event.data);
    userFunction();
  } catch(error){
    if(error instanceof Error){
      return {
        code: '',
        error: error.message
      }
    } else {
      throw error;
    }
  }
}, false);

注意事项

  • 不同浏览器的Error栈信息格式存在差异,需要针对Chrome、Firefox、Safari等主流浏览器做兼容调整。
  • 使用Function替代eval不仅能优化行号获取的准确性,还能避免eval带来的作用域污染问题,性能表现也更优。

内容的提问来源于stack exchange,提问作者Francisco Martin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:38:09