如何找出 JavaScript 中函数的调用位置?

问题描述 投票:0回答:7

是否可以找出函数是从哪里调用的?如果是,那么 如何检测函数是否从全局作用域、从另一个函数或从浏览器控制台调用?

看下面的例子:

<script>
    function myFunc1() {
        // some code
        myFunc2(); // I was called from myFunc1()
    }
    function myFunc2() {
        var callerName = new String;
        callerName = arguments.callee.caller.name;
        // some code
        alert('I was called from ' + callerName + ' function');
    }
    myFunc2(); // I was called from global scope
</script>

我知道上面示例中的这一行

callerName = arguments.callee.caller.name;
会给我调用者函数的名称。但我不知道如何检测函数是否是从全局范围调用的。例如,如果我更改
 myFunc2()
并添加一个
if else
语句来检查
arguments.callee.caller.name
是否返回
undefined
值,知道当从全局范围调用函数时会发生这种情况:

myFunc2() {
var callerName = new String;
callerName = arguments.callee.caller.name;
    if(callerName == undefined) {
        alert('I was called from global scope');
    } else {
        alert('I was called from ' + callerName + ' function');
    }
}

但是,如果从全局范围调用

myFunc2()
,则这将不起作用,并且
callerName = arguments.callee.caller.name;
将导致 JavaScript 抛出以下错误:

TypeError: 'null' is not an object (evaluating 'arguments.callee.caller.name')

所以我回到了第一点,问题仍然存在:

  • 如何检测函数是否是从全局范围调用的?
  • 如果从全局范围调用,是从 浏览器控制台调用吗?
javascript function call
7个回答
35
投票

在 Chrome 中,您可以使用:

console.trace();

只需在函数中添加该行即可,我通常将其放置在第一行。如果您查看控制台,您将看到函数的名称,在该行下方您将看到它的调用位置。


8
投票

如果从全局范围调用该函数,

arguments.callee.caller.name
将是未定义的。否则,它将是调用者函数的名称(也代表调用它的范围)。

所以你已经拥有的应该可以工作,除了在严格模式,其中

arguments.callee
不可用。


此外:浏览器中提供的开发人员工具可能是检查此类事情的更好方法:只需设置一个断点并查看堆栈跟踪面板。当然,除非您的代码本身需要知道运行时的调用范围。


4
投票

根据您的浏览器,您可能会引发错误,例如通过访问

try/catch
内的未定义变量。然后检查一些浏览器在错误中提供的堆栈跟踪。

这将是非常特定于浏览器的。


2
投票

function func1() {
    // some code
    func2(); // I was called from func1()
}
function func2() {
var callerName = new String;
callerName = arguments.callee.caller ? arguments.callee.caller.name : "global";
    if(callerName == "global") {
        alert('I was called from global scope');
    } else {
        alert('I was called from ' + callerName + ' function');
    }
}
func1();
func2(); // I was called from global scope

func2 被调用,它将显示其调用者名称,如果在另一个函数范围内调用它,它将显示该函数的名称,如果从全局范围调用它,它将显示一条消息,表明它是从全局范围调用的。


1
投票

这是一个相当棘手的问题。正如此答案中所讨论的,Arguments.callee 在某些浏览器中已被弃用。

根据您的方法,您可以使用 Function.caller。 我写了一个非常简单的例子

function A() {
    if (A.caller === null || A.caller.name == "") console.log("Function called from top window")
    else console.log("Called from " + A.caller.name);
    B();
}

function B() {
    if (B.caller === null || B.caller.name == "") console.log("Function called from top window")
    else console.log("Called from " + B.caller.name);
}

A();

可以在here

找到另一个更通用的示例

仍然

'use strict'
使这两种方法都失败,因此使用
try / catch
块可以解决您的问题,或者您可以为您的函数模拟“虚拟调用堆栈”(取决于所有代码同步工作的事实)


1
投票

在 JavaScript 中,大多数东西都是对象,当你声明

callerName = new String
时,你创建了一个带有一些属性和方法的 string 对象。例如,
valueOf()
方法将返回字符串对象的 原始值。 但是,就像 JavaScript 告诉您
'TypeError: 'null' is not an object
null 不是一个对象,而是,它不存在对象。 null 没有任何方法或属性。 当从全局范围调用函数时,
arguments.callee.caller
会将调用者计算为 null。 所以
arguments.callee.caller.name
就像尝试访问 null 的
name
属性 (
null.name
)
,但 null 没有名为
name
,
的属性,因为它不是对象,并且 不能 根本没有任何属性。这就是 JavaScript 抱怨的原因,因为你试图访问不存在的东西。但是你可以做的是首先使用简单的 caller
 
检查
if else
 是否为假值
语句
if(!arguments.callee.caller)
如果不是,那么你可以访问
name
caller
属性并找出哪个函数调用了
myFunc2()
但是如果是,那么你就知道这个函数了是从全局范围内调用的。

function myFunc2() {
    if(!arguments.callee.caller) {
        callerName = "global";
        alert('I was called from global scope');
    } else {
        callerName = arguments.callee.caller.name;
        alert('I was called from ' + callerName + ' function');
    }
}

0
投票

您可以创建一个 Error 实例并在函数内调用其 stack 方法来查看调用堆栈

const stack = new Error().stack()
console.log(stack)

上面两行代码应该在函数体内,一个例子是

function calculateAge() {
  const stack = new Error().stack();
  console.log(stack);
}
© www.soinside.com 2019 - 2024. All rights reserved.