ilgaar
ilgaar

Reputation: 844

How to find out where a function is called from in JavaScript?

Is it possible to find out where a function is called from? If yes, then how to detect, if a function is called from a global scope, from another function, or perhaps from a browser console?

Take a look at the following example:

<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>

I know that this line callerName = arguments.callee.caller.name;in the example above, would give me caller function's name. But I don't know how to detect if a function was called from a global scope. For instance if I change myFunc2() and add an if else statement to check if arguments.callee.caller.name returns an undefined value, knowing that this will happen, when a function is called from a global scope:

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');
    }
}

However, this will not work if myFunc2() is called from a global scope and callerName = arguments.callee.caller.name; will cause JavaScript to throw the following error:

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

So I am back to square one, and the question still remains:

Upvotes: 31

Views: 24409

Answers (7)

Meer Estiyak
Meer Estiyak

Reputation: 11

You might just create an instance of Error & call its stack method inside the function to see the call stack

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

above two lines of code should be inside the function body, an example might be

function calculateAge() {
  const stack = new Error().stack();
  console.log(stack);
}

Upvotes: 0

Gene Parcellano
Gene Parcellano

Reputation: 6044

In Chrome, you can use:

console.trace();

Just add that line in your function, I usually place it as the first line. If you view the console you'll see the name of your function, and below that line you'll see where it's being called from.

Upvotes: 40

ilgaar
ilgaar

Reputation: 844

In JavaScript most things are objects, when you declare callerName = new String you create a string object with some properties and methods. For instance, the valueOf() method will return the primitive value of a string object. However, just like JavaScript tells you 'TypeError: 'null' is not an object null is not an object but rather, it's the absence of an object. null doesn't have any methods or properties. When a function is called from a global scope, arguments.callee.caller evaluates caller to null. Soarguments.callee.caller.name is like trying to access null's name property (null.name), but null doesn't have a property called name, since it's not an object and can not have any property at all. This is why JavaScript complains, because you are trying to access something that doesn't exist. What you can do however, is to first check if caller is a falsy value by using a simple if else statement if(!arguments.callee.caller), if it isn't, then you can access the name property of the caller and find out what function has called myFunc2(), but if it is, then you know the function was called from a global scope.

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');
    }
}

Upvotes: 2

vickisys
vickisys

Reputation: 2036

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 is called it will display its caller name, if it was called within another function scope, it will display that function's name and if it was called from global scope it will display a message that shows it was called from the global scope.

Upvotes: 3

vorillaz
vorillaz

Reputation: 6266

That's quite a tricky question. As discussed on this answer Arguments.callee is deprecated in some browsers.

Depending on your approach you can use Function.caller. I have written a quite simple example

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();

Another more generic example can be found here

Still 'use strict' makes both these approaches fail, so using a try / catch block can solve your problem or you may simulate a "dummy call stack" for your functions ( depending on the fact that all your code works synchronously )

Upvotes: 1

HBP
HBP

Reputation: 16033

Depending on your browser, you could provoke and error, by accessing an undefined variable for example, inside a try/catch. Then examine the stack trace some browsers provide in the error.

This will be very browser specific.

Upvotes: 4

bfavaretto
bfavaretto

Reputation: 71908

If the function is called from the global scope, arguments.callee.caller.name will be undefined. Otherwise, it will be the name of the caller function (which also represents the scope it was called from).

So what you already have should work, except in strict mode, where arguments.callee is not available.


Additionaly: the developer tools available from your browser are probably a better way to inspect this kind of thing: just set a breakpoint and look at the stack trace panel. Unless of course your code itself needs to know the calling scope at runtime.

Upvotes: 8

Related Questions