如何获得JavaScript调用者函数行号?如何获取JavaScript调用者源URL?


109

我正在使用以下代码来获取JavaScript调用者函数的名称:

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

有没有办法发现调用该方法的行号?

另外,是否有办法获取调用该方法的JavaScript文件的名称?还是源URL?


2
我认为这在IE中是不可能的,否则我们将有办法解决没有提供任何细节的CRAPPY错误消息。但是,如果有可能,我也想知道!
Zoidberg 2009年

是。这是一个跨浏览器功能,它利用了每种浏览器的专有方法:github.com/eriwen/javascript-stacktrace [fixed link]
斯科特(Scotts)2010年

Answers:


99

这适用于我在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()的任何位置获取行号。
mikemaccana 2012年

60
不需要抛出错误;只需创建就足够了:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE

2
将这个建议与另一个类似的答案合并,以获得FF / Webkit“标准化”响应-请参阅stackoverflow.com/a/14841411/1037948
drzaus 2013年

1
在PhantomJS中也可以使用,但是您必须将其抛出,否则将不会在错误上设置“ stack”属性。
约书亚·理查森

1
实际上,@ ELLIOTTCABLE在某些浏览器(如iOS Safari)中确实需要引发异常!那么为什么不这样做呢?
arctelix

26

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


11
嗨,谢谢你的插件。您知道是否可以从上次通话中获取电话号码吗?假设方法A调用了B,现在BI中的用户想知道在A下的哪一行进行了调用?
Tal

85
这已打勾,但没有回答问题,即如何获取调用方函数的行号。
mikemaccana 2012年

3
而且,这是非常有限的。最好的解决方案是抛出一个错误,并在error.stack上使用正则表达式,所有现代浏览器都提供该表达式。您可以轻松提取该路径,文件,行和列。没问题。
arctelix

13

令我惊讶的是,大多数答案都假定您要处理错误,而不仅仅是在正常情况下也输出有用的调试跟踪。

例如,我喜欢使用这样的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/,以及具有正确行号的console.log的正确包装?


1
适用于firefox浏览器,但不适用于node.js。
拉链

1
在节点下,您必须登录堆栈[2]
monika mevenkamp

5

这通常是通过从当前上下文抛出错误来实现的。然后分析错误对象的属性,例如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第三版中从未如此)。

还要记住,函数反编译被指定为依赖于实现,因此可能会产生意想不到的结果。我在这里这里都写过。


谢谢,在我需要的应用程序中添加此代码似乎有点麻烦。(某些js跟踪框架)您是否知道我可以使用任何其他不建议使用的方法?
塔拉2009年

您应该能够检查错误对象,而不必依赖不推荐使用的对象callee.caller
kangax

您不需要抛出错误。您只需使用(new Error).lineNumber即可访问脚本中的当前行号。
伊莱·格雷

@Elijah这就是我在FF3中看到的。另一方面,WebKit line仅在引发错误时填充。
kangax

callee.caller的替代品是什么?是否需要获取函数名称?
Tal

4

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

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的错误控制台中,显示的代码行号(和文件名)是抛出“ Error”异常而不是您真正想要进行调试的“ 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();
}

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


4

行号实际上是静态的,因此,如果您只想将其用于记录,则可以使用诸如gulp之类的东西对其进行预处理。我写了一个小gulp 插件,正是这样:

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时),这似乎是一个很好的建议。谢谢!
安迪·金


3

如果您想知道行号用于调试目的,或者仅在开发过程中(出于某种原因),则可以使用Firebug(Firefox扩展)并引发异常。

编辑

如果出于某种原因确实需要在生产中执行此操作,则可以对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);
}

1
谢谢,出于支持的原因,我想在生产中使用此功能。我找到了一些代码,使您可以查看直到该方法之前所有流程的调用堆栈,但其中没有调用方法的行号。我想为此简单解决?
塔尔2009年

3

我就是这样做的,我已经在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);
    }
}

2

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

这是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报告为undefined。


2

以下代码在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) {
spiderplant0

我喜欢这个主意,但是行号对我来说不正确。
瑞安

2

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

  1. 首先,我同意@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/



1

要确定某行在哪一行,您必须在所有代码中搜索占据特定关注行的代码,并从顶部开始到该关注行计算“ \ n”字符,然后加1。

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

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


特定的兴趣点可能很多...如果我有一个方法被另一个方法多次调用,我如何知道(在另一个方法中)调用从何而来?
塔尔2009年

您将无法在执行时从解释器外部分析JavaScript解释。您可以编写一个程序来跟踪程序中的执行路径,但是执行的将是该程序,而不是您要分析的代码。这通常是一项复杂的任务,需要借助工具手动执行。如果您真的想在执行代码时看到代码中发生了什么,那么就让它将元数据写入屏幕,告诉您希望决策执行哪些其他部分。

-2

答案很简单。不,不(不)。

到了javascript运行时,源文件/ URL的概念就消失了。

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

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


在Firefox中,例外情况包括此类信息...至少在Firefox中可能吗?
Zoidberg

当启动MS Script调试器并放置一些断点时,您会在调用堆栈中看到确切的来源。这是因为有专门的钩子吗?
塔尔2009年

“再次在执行时,代码“行”的概念在Javascript中不再有意义。” ??每次您运行console.log(),JS已经显示行号
mikemaccana 2012年

@AnthonyWJones是的。它与绝对的“否和否(No)”相矛盾。
mikemaccana 2012年

@nailer:早在2009年,所有主要浏览器中,我的回答都与哪种方式相矛盾。请记住,当前的问题是关于在运行javascript中发现被叫方的电话号码?
AnthonyWJones
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.