是否可以找出函数是从哪里调用的?如果是,那么 如何检测函数是否从全局作用域、从另一个函数或从浏览器控制台调用?
看下面的例子:
<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')
所以我回到了第一点,问题仍然存在:
在 Chrome 中,您可以使用:
console.trace();
只需在函数中添加该行即可,我通常将其放置在第一行。如果您查看控制台,您将看到函数的名称,在该行下方您将看到它的调用位置。
如果从全局范围调用该函数,
arguments.callee.caller.name
将是未定义的。否则,它将是调用者函数的名称(也代表调用它的范围)。
所以你已经拥有的应该可以工作,除了在严格模式,其中
arguments.callee
不可用。
此外:浏览器中提供的开发人员工具可能是检查此类事情的更好方法:只需设置一个断点并查看堆栈跟踪面板。当然,除非您的代码本身需要知道运行时的调用范围。
根据您的浏览器,您可能会引发错误,例如通过访问
try/catch
内的未定义变量。然后检查一些浏览器在错误中提供的堆栈跟踪。
这将是非常特定于浏览器的。
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 被调用,它将显示其调用者名称,如果在另一个函数范围内调用它,它将显示该函数的名称,如果从全局范围调用它,它将显示一条消息,表明它是从全局范围调用的。
这是一个相当棘手的问题。正如此答案中所讨论的,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
块可以解决您的问题,或者您可以为您的函数模拟“虚拟调用堆栈”(取决于所有代码同步工作的事实)
在 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');
}
}
您可以创建一个 Error 实例并在函数内调用其 stack 方法来查看调用堆栈
const stack = new Error().stack()
console.log(stack)
上面两行代码应该在函数体内,一个例子是
function calculateAge() {
const stack = new Error().stack();
console.log(stack);
}