【问题标题】:How to get JavaScript caller function line number and caller source URL如何获取 JavaScript 调用函数行号和调用源 URL
【发布时间】:2010-11-23 09:00:55
【问题描述】:

我正在使用以下方法获取 JavaScript 调用函数名称:

var callerFunc = arguments.callee.caller.toString();
callerFuncName = (callerFunc.substring(callerFunc.indexOf("function") + 8, callerFunc.indexOf("(")) || "anoynmous")

有没有办法找出调用该方法的行号?

另外,有没有办法获取调用该方法的 JavaScript 文件的名称?还是来源网址?

【问题讨论】:

  • 我认为这在 IE 中是不可能的,否则我们将有办法绕过那里没有提供细节的糟糕错误消息。但如果可能的话,我也很想知道!
  • 是的。这是一个跨浏览器功能,它利用了每个浏览器的专有方法:github.com/eriwen/javascript-stacktrace [固定链接]

标签: javascript


【解决方案1】:

如果您需要非常完整和准确的东西,对于 v8(意味着 NodeJS 和 Chrome)有 stack-trace 包,它对我有用:

https://www.npmjs.com/package/stack-trace

这有几个优点:能够产生深度堆栈跟踪,能够遍历 async 调用,以及在每个堆栈帧处提供脚本的完整 URL。

您可以找到有关 v8 堆栈跟踪 API here 的更多信息。

如果您需要在更多浏览器中工作的东西,有这个包:

https://www.npmjs.com/package/stacktrace-js

该库解析您通常在 devtools 控制台中看到的格式化堆栈跟踪字符串,这意味着它只能访问实际出现在格式化堆栈跟踪中的信息 - 与 v8 API 不同,您无法获取(在其他东西)堆栈框架中脚本的完整 URL。

请注意,解析格式化的堆栈跟踪是一种有点脆弱的方法,因为这种格式可能会在浏览器中发生变化,这会破坏您的应用程序。

据我所知,跨浏览器没有比这更安全的选择了 - 如果您需要在所有浏览器中都能运行的快速、准确和完整的东西,那么您就很不走运了。

【讨论】:

    【解决方案2】:

    过去更容易,但这次有了浏览器更新:

    这是安全的多浏览器解决方案

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <script>
            var lastErr;
            function errHand(e) {
                lastErr = e;
                switch (e.target.nodeName) {
                    case 'SCRIPT':
                        alert('script not found: ' + e.srcElement.src);
                        break;
                    case 'LINK':
                        alert('css not found: ' + e.srcElement.href);
                }
                return false;
            }
            window.onerror = function (msg, url, lineNo, columnNo, error) {
                alert(msg + ' - ' + url + ' - ' + lineNo + ' - ' + columnNo);
                return false;
            }
        </script>
        <script src="http://22.com/k.js" onerror="errHand(event)"></script>
        <link rel="stylesheet" href="http://22.com/k.css" onerror="errHand(event)" type="text/css" />
    </head>
    <body>
        <script>
            not_exist_function();
        </script>
    </body>
    </html>
    
    1. 不要尝试覆盖或阅读控制台浏览器自动生成的错误日志,目前不工作,但在过去 > 它发生了
    2. 对于加载每个 external 链接的 css/js/etc 的控件,请使用 onerror 事件属性 innertag
    3. 对于控制内联/加载的脚本,请使用 window.onerror
    4. 将错误处理函数放在 html 代码的顶部
    5. 要为“include”等其他特殊标签开发此代码,请设置 onerror 内联事件,如 step 2 并在 fire 错误后使用 console.log(lastErr) 查看错误对象字段
    6. 如果 附加的 js 文件的来源有错误,则 window.onerror 仅在 js 具有 同源主机 时获取完整日志strong> 与主 html 文件,如果您在没有主机的 local 上测试这种情况,那么日志 notcomplete 因为 local-no-host 环境不属于同源
    7. 对于像 Ajax/Websocket 这样的发送 Request 错误处理,最好使用它们的内置错误处理函数

    对于 NodeJS - 全局错误处理解决方案

    process.on('uncaughtException', function (err) {
        console.error('uncaughtException:\n' + err.stack + '\n');
    })
    

    非常有用,因为它会向您显示存在但不会破坏您的主程序进程的错误。

    【讨论】:

      【解决方案3】:

      令我惊讶的是,这些答案中的大多数都假设您想要处理错误,而不是仅仅为正常情况输出有用的调试跟踪。

      例如,我喜欢像这样使用console.log 包装器:

      consoleLog = function(msg) {//See https://stackoverflow.com/a/27074218/470749
          var e = new Error();
          if (!e.stack) {
              try {
                  // IE requires the Error to actually be thrown or else the 
                  // Error's 'stack' property is undefined.
                  throw e;
              } catch (e) {
                  if (!e.stack) {
                      //return 0; // IE < 10, likely
                  }
              }
          }
          var stack = e.stack.toString().split(/\r\n|\n/);
          if (msg === '') {
              msg = '""';
          }
          console.log(msg, '          [' + stack[1] + ']');        
      }
      

      这最终会在我的控制台上打印如下输出:

      1462567104174 [getAllPosts@http://me.com/helper.js:362:9]

      https://stackoverflow.com/a/27074218/A proper wrapper for console.log with correct line number?

      【讨论】:

      • 适用于 firefox 浏览器,但不适用于 node.js。
      • 在节点下你必须记录堆栈[2]
      • 有没有人测试过这段代码,看看它是否有效? if 条件if (!e.stack) 的作用域是什么?这看起来像是一个 python 程序员的指纹;-)
      • @puk 我从来不喜欢 Python,但正如我在上面的代码评论中提到的,我是从 stackoverflow.com/a/27074218/470749 那里得到的,我不能说那个人是否喜欢 Python。但是,是的,这段代码在 2016 年对我有用。不过我已经好几年没用过了。 stackoverflow.com/a/55392553/470749 看起来很有趣。
      • @Ryan 我认为解析如下:if (!e.stack)try{...}catch{...} 只是悬空。有没有专业 js 的人想解析上面的代码?我是一个 C 人,所以对我来说,这应该会在那个悬空的 catch 语句中引发错误
      【解决方案4】:

      我意识到这是一个老问题,但现在有一个名为console.trace("Message") 的方法会显示行号和导致日志的方法调用链以及您传递的消息。有关 javascript 日志记录技巧的更多信息,请访问 here at freecodecampthis medium blog post

      【讨论】:

        【解决方案5】:
        console.log(new Error);
        

        它将向您展示整个曲目。

        【讨论】:

          【解决方案6】:

          这就是我的做法,我已经在 Firefox 和 Chrome 中测试过了。这样可以检查调用函数的位置的文件名和行号。

          logFileAndLineNumber(new Error());
          
          function logFileAndLineNumber(newErr)
          {
             if(navigator.userAgent.indexOf("Firefox") != -1)
             {
                var originPath = newErr.stack.split('\n')[0].split("/");
                var fileNameAndLineNumber = originPath[originPath.length - 1].split(">")[0];
                console.log(fileNameAndLineNumber);
             }else if(navigator.userAgent.indexOf("Chrome") != -1)
             {
                var originFile = newErr.stack.split('\n')[1].split('/');
                var fileName = originFile[originFile.length - 1].split(':')[0];
                var lineNumber = originFile[originFile.length - 1].split(':')[1];
                console.log(fileName+" line "+lineNumber);
              }
          }
          

          【讨论】:

            【解决方案7】:

            行号实际上是静态的,所以如果你只是想要它来记录,那么它可以用 gulp 之类的东西进行预处理。我已经写了一个小的 gulp plugin 就是这样做的:

            var gulp = require('gulp');
            var logLine = require('gulp-log-line');
            gulp.task('log-line', function() {
                return gulp.src("file.js", {buffer : true})
                //Write here the loggers you use.
                    .pipe(logLine(['console.log']))
                    .pipe(gulp.dest('./build'))
            
            })
            
            gulp.task('default', ['log-line'])
            

            这会将文件名和行附加到来自 console.log 的所有日志中,因此 console.log(something) 将变为 console.log('filePath:fileNumber', something)。优点是现在你可以连接你的文件,转译它们......你仍然会得到这条线

            【讨论】:

            • 对于使用转译器的情况(例如,使用 TypeScript 时),这似乎是一个很好的建议。谢谢!
            【解决方案8】:

            我对 JavaScript 中自定义错误的贡献:

            1. 首先,我同意Inheriting from the Error object - where is the message property? 的这个@BT 家伙,我们必须正确构建它(实际上你必须使用js 对象库,我最喜欢:https://github.com/jiem/my-class):

              window.g3 = window.g3 || {};
              g3.Error = function (message, name, original) {
                   this.original = original;
                   this.name = name || 'Error.g3';
                   this.message = message || 'A g3.Error was thrown!';
                   (original)? this.stack = this.original.stack: this.stack = null;
                   this.message += '<br>---STACK---<br>' + this.stack;
               };
              
               var ClassEmpty = function() {};
               ClassEmpty.prototype = Error.prototype;
               g3.Error.prototype = new ClassEmpty();
               g3.Error.prototype.constructor = g3.Error;
              
            2. 然后,我们应该定义一个全局错误处理函数(可选),否则它们将最终交给引擎:

              window.onerror = printError; 
              function printError(msg, url, line){
                  document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
                  return true;
              }
              
            3. 最后,我们应该小心抛出自定义错误:

              //hit it!
              //throw new g3.Error('Hey, this is an error message!', 'Error.Factory.g3');
              throw new g3.Error('Hey, this is an error message!', 'Error.Factory.g3', new Error());
              

            只有在将第三个参数作为new Error() 传递时,我们才能看到带有函数和行号的堆栈!

            在2处,该函数也可以处理引擎抛出的错误。

            当然,真正的问题是我们是否真的需要它以及何时需要它;在某些情况下(我认为 99%),优雅地返回 false 就足够了,只留下一些关键点来显示抛出错误。

            例如:http://jsfiddle.net/centurianii/m2sQ3/1/

            【讨论】:

              【解决方案9】:

              以下代码适用于我在 Mozilla 和 Chrome 中。

              它的日志功能,显示文件的名称和调用者的行。

              log: function (arg) {
                  var toPrint = [];
                  for (var i = 0; i < arguments.length; ++i) {
                      toPrint.push(arguments[i]);
                  }
              
                  function getErrorObject(){
                      try { throw Error('') } catch(err) { return err; }
                  }
              
                  var err = getErrorObject(),
                      caller;
              
                  if ($.browser.mozilla) {
                      caller = err.stack.split("\n")[2];
                  } else {
                      caller = err.stack.split("\n")[4];
                  }
              
                  var index = caller.indexOf('.js');
              
                  var str = caller.substr(0, index + 3);
                  index = str.lastIndexOf('/');
                  str = str.substr(index + 1, str.length);
              
                  var info = "\t\tFile: " + str;
              
                  if ($.browser.mozilla) {
                      str = caller;
                  } else {
                      index = caller.lastIndexOf(':');
                      str = caller.substr(0, index);
                  }
                  index = str.lastIndexOf(':');
                  str = str.substr(index + 1, str.length);
                  info += " Line: " + str;
                  toPrint.push(info);
              
                  console.log.apply(console, toPrint);
              }
              

              【讨论】:

              • 好像少了点什么。我得到:SyntaxError: function statement requires a name,log: function (arg) {
              • 我喜欢这个主意,但行号对我来说不正确。
              【解决方案10】:

              这是我根据这个论坛上的信息写的:

              这是 MyDebugNamespace 的一部分,Debug 显然是保留的,不会用作命名空间名称。

                  var DEBUG = true;
              

              ...

                  if (true == DEBUG && !test)
                  {
                      var sAlert = "Assertion failed! ";
                      if (null != message)
                          sAlert += "\n" + message;
                      if (null != err)
                          sAlert += "\n" + "File: " + err.fileName + "\n" + "Line: " + err.lineNumber;
                      alert(sAlert);
                  }
              

              ...

              如何调用:

                  MyDebugNamespace.Assert(new Error(""), (null != someVar), "Something is wrong!")
              

              我在我的命名空间中包含了两个具有可变数量参数的函数调用此基本代码,以便选择性地忽略调用中的消息或错误。

              这适用于 Firefox、IE6 和 Chrome 报告 fileName 和 lineNumber 为未定义。

              【讨论】:

                【解决方案11】:

                看来我有点晚了 :),但是讨论很有趣所以.. 就这样吧...假设您要构建一个错误处理程序,并且您正在使用自己的异常处理程序类,例如:

                function  errorHandler(error){
                    this.errorMessage = error;
                }
                errorHandler.prototype. displayErrors = function(){
                    throw new Error(this.errorMessage);
                }
                

                你正在像这样包装你的代码:

                try{
                if(condition){
                    //whatever...
                }else{
                    throw new errorHandler('Some Error Message');
                }
                }catch(e){
                    e.displayErrors();
                }
                

                您很可能会将错误处理程序放在单独的 .js 文件中。

                您会注意到,在 firefox 或 chrome 的错误控制台中,显示的代码行号(和文件名)是引发“错误”异常的行(文件),而不是您真正想要的“errorHandler”异常使调试变得容易。抛出你自己的异常很好,但在大型项目中定位它们可能是一个相当大的问题,特别是如果它们有类似的消息。因此,您可以做的是将对实际空 Error 对象的引用传递给您的错误处理程序,该引用将包含您想要的所有信息(例如,在 Firefox 中,您可以获得文件名和行号等。 ; 在 chrome 中,如果您读取 Error 实例的 'stack' 属性,您会得到类似的东西)。 长话短说,你可以这样做:

                function  errorHandler(error, errorInstance){
                    this.errorMessage = error;
                    this. errorInstance = errorInstance;
                }
                errorHandler.prototype. displayErrors = function(){
                    //add the empty error trace to your message
                    this.errorMessage += '  stack trace: '+ this. errorInstance.stack;
                    throw new Error(this.errorMessage);
                }
                
                try{
                if(condition){
                    //whatever...
                }else{
                    throw new errorHandler('Some Error Message', new Error());
                }
                }catch(e){
                    e.displayErrors();
                }
                

                现在您可以获得引发自定义异常的实际文件和行号。

                【讨论】:

                  【解决方案12】:

                  这在 chrome/QtWebView 中适用于我

                  function getErrorObject(){
                      try { throw Error('') } catch(err) { return err; }
                  }
                  
                  var err = getErrorObject();
                  var caller_line = err.stack.split("\n")[4];
                  var index = caller_line.indexOf("at ");
                  var clean = caller_line.slice(index+2, caller_line.length);
                  

                  【讨论】:

                  • 也可以在节点中使用。在您的自定义 log() 函数中弹出它(它添加了您需要的任何其他有用的解决方法 - 例如为 Chrome 数组日志记录修复)并且仍然是您调用 log() 的任何位置的行号。
                  • 不需要抛出错误;只需创建它就足够了:var caller_line = (new Error).stack.split("\n")[4]
                  • 将此建议与另一个类似答案合并以获得 FF/Webkit “标准化”响应——请参阅 stackoverflow.com/a/14841411/1037948
                  • 也可以在 PhantomJS 中使用,但是你必须抛出它,否则错误上没有设置“stack”属性。
                  • @ELLIOTTCABLE 实际上在某些浏览器(如 iOS Safari)中,您确实需要抛出异常!那么为什么不这样做呢?
                  【解决方案13】:

                  这通常是通过从当前上下文中抛出错误来实现的;然后分析错误对象的属性,如lineNumberfileName(某些浏览器有)

                  function getErrorObject(){
                    try { throw Error('') } catch(err) { return err; }
                  }
                  
                  var err = getErrorObject();
                  
                  err.fileName;
                  err.lineNumber; // or `err.line` in WebKit
                  

                  不要忘记 callee.caller 属性已被弃用(并且从未真正在 ECMA 第 3 版中。首先)。

                  还请记住,函数反编译被指定为依赖于实现,因此可能会产生非常意想不到的结果。我写了herehere

                  【讨论】:

                  • 谢谢,在我需要的应用程序中添加这段代码似乎有点问题。 (一些 js 跟踪框架)你知道我可以使用的任何其他不被弃用的方法吗?
                  • 您应该能够检查错误对象,而无需依赖已弃用的callee.caller
                  • 您不需要抛出错误。您只需使用 (new Error).lineNumber 来访问脚本中的当前行号。
                  • @Elijah 这就是我在 FF3 中看到的。另一方面,WebKit 仅在引发错误时才会填充 line
                  • callee.caller 的替代品是什么?如果我需要获取函数名?
                  【解决方案14】:

                  kangax 的解决方案引入了不必要的 try..catch 范围。如果您需要在 JavaScript 中访问某些内容的行号(只要您使用的是 Firefox 或 Opera),只需访问 (new Error).lineNumber

                  【讨论】:

                  • 嗨,谢谢你的插件。你知道是否有可能从以前的电话中获得行号?假设方法 A 调用 B ,现在在 B 中,我想知道在 A 下的哪一行进行了调用?
                  • 这个打勾了,但不回答问题,即如何获取调用者函数的行号
                  • 另外,这是极其有限的。最好的解决方案是抛出错误并在所有现代浏览器中都可用的 error.stack 上使用正则表达式。您可以轻松提取该路径、文件、行和列。没问题。
                  【解决方案15】:

                  如果您想知道行号以用于调试目的,或者仅在开发期间(出于某种原因),您可以使用Firebug(Firefox 扩展)和throw 例外。

                  编辑

                  如果出于某种原因您确实需要在生产中执行此操作,您可以预处理您的 javascript 文件,以便每个函数跟踪它所在的行。我知道一些找到代码覆盖率的框架使用这个(例如JSCoverage)。

                  例如,假设您的原始调用是:

                  function x() {
                    1 + 1;
                    2 + 2;
                    y();
                  }
                  

                  你可以写一个预处理器把它变成:

                  function x() {
                    var me = arguments.callee;
                    me.line = 1;
                    1 + 1;
                    me.line = 2;
                    2 + 2;
                    me.line = 3;
                    y();
                  }
                  

                  那么在y()中,你可以使用arguments.callee.caller.line知道它是从哪一行调用的,比如:

                  function y() {
                    alert(arguments.callee.caller.line);
                  }
                  

                  【讨论】:

                  • 谢谢,出于支持的原因,我想在生产中使用它。我找到了一些代码,可以让您查看直到方法的所有流的调用堆栈,但它没有调用方法的行号。我想这很容易解决?
                  【解决方案16】:

                  要确定某行在哪一行,您必须在所有代码中搜索占据特定感兴趣行的代码,并从顶部到感兴趣的行数“\n”字符并加 1。

                  我实际上是在我正在编写的应用程序中做这件事。它是 HTML 的最佳实践验证器,并且仍在大量开发中,但您会感兴趣的错误输出过程已经完成。

                  http://mailmarkup.org/htmlint/htmlint.html

                  【讨论】:

                  • 感兴趣的特定行可能有很多...如果我从另一个方法多次调用相同的方法,我怎么知道(在那个其他方法中)调用是从哪里来的?跨度>
                  • 您将无法在执行时从解释器外部分析 JavaScript 解释。您可以编写一个程序来跟踪程序中的执行路径,但执行的将是该程序,而不是您要分析的代码。这通常是一项复杂的任务,需要借助工具手动执行。如果您真的想在代码执行时查看代码中发生了什么,那么让它将元数据写入屏幕,告诉您希望决策正在执行哪些其他部分。
                  【解决方案17】:

                  答案很简单。没有和没有(否)。

                  到 javascript 运行时,源文件/url 的概念已经不复存在了。

                  也无法确定行号,因为在执行时代码“行”的概念在 Javascript 中不再有意义。

                  特定实现可能会提供 API 挂钩,以允许特权代码访问此类详细信息以进行调试,但这些 API 不会暴露给普通的标准 Javascript 代码。

                  【讨论】:

                  • 在 Firefox 中,例外情况包括此类信息...至少在 Firefox 中可能吗?
                  • 当您启动 MS Script 调试器并放置一些断点时,您会在调用堆栈中看到您的确切来源。这是因为专门的钩子吗?
                  • “再次在执行时,代码“行”的概念在 Javascript 中不再有意义。”嗯?每次运行 console.log() 时 JS 都会显示行号
                  • @AnthonyWJones 是的。它巧妙地与有些绝对的“不和不(No)”相矛盾。
                  • @nailer:回到 2009 年所有主流浏览器,我的回答有什么矛盾之处。请记住,手头的问题是关于在运行 javascript 时发现被调用者的行号?
                  猜你喜欢
                  • 2014-08-17
                  • 2014-01-28
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-02-25
                  • 2012-08-04
                  • 2021-03-11
                  • 2014-03-15
                  • 2019-02-19
                  相关资源
                  最近更新 更多