Recently, I was writing a JS reverse hook plug-in, and then I need to get the location of the current code execution to facilitate code positioning, so I'll summarize JavaScript How to output the function call stack.
Demo code
function main() { let a = fun('hello world') console.log(a) } function fun(a) { return a } main() Copy code
method
console.trace()
Use the following
function main() { let a = fun('hello world') console.log(a) } function fun(a) { console.trace('fun') return a } main() Copy code
The output result is
Trace: fun at fun (c:\Users\zeyu\Desktop\demo\main.js:7:11) at main (c:\Users\zeyu\Desktop\demo\main.js:2:11) at Object.<anonymous> (c:\Users\zeyu\Desktop\demo\main.js:11:1) at Module._compile (node:internal/modules/cjs/loader:1095:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10) at Module.load (node:internal/modules/cjs/loader:975:32) at Function.Module._load (node:internal/modules/cjs/loader:816:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12) at node:internal/main/run_main_module:17:47 hello world Copy code
console.trace() can pass in parameters, which will be directly output after trace, such as fun here, but can only be output in the console
But IE6 doesn't support it, but no one should use it
arguments.callee.caller
In the non strict mode, you can directly output arguments, and then print the called parameters and the called function, as follows
function main() { let a = fun('hello world') console.log(a) } function fun(a) { console.log(fun.caller.toString()) console.log(arguments) console.log(arguments.callee.toString()) console.log(arguments.callee.caller.toString()) return a } main() Copy code
The output result is
function main() { let a = fun('hello world') console.log(a) } [Arguments] { '0': 'hello world' } function fun(a) { console.log(fun.caller.toString()) console.log(arguments) console.log(arguments.callee.toString()) console.log(arguments.callee.caller.toString()) return a } function main() { let a = fun('hello world') console.log(a) } hello world Copy code
We have successfully printed out the currently running functions (toString is used here to print out the functions), and the superior functions can be obtained through fun.caller and arguments.callee.caller.
Caller is the upper level function called, that is, the main function here. It is not difficult to find that there is a caller attribute under each caller object, that is, the upper level function of caller. Because I have a node environment here, I don't know what the caller of caller here is... Anyway, this is not the focus of attention. The focus is that * * fun.caller and ` arguments.callee.caller can print the upper function * * until the caller is empty
The [[FunctionLocation]] in the other outer ring is the location of the function. Unfortunately, this is not a caller attribute and is only used by the js engine, so it cannot be output.
To sum up:
fun.caller == arguments.callee.caller represents the execution environment of fun (upper level function)
arguments.callee represents the fun being executed
Premise: in non strict mode
new Error().stack
As we all know, once the program makes an error, it will directly stop running and output error information. The error information here includes the called function and specific location. Compared with the above methods, this can be directly output in the execution environment rather than simply displayed on the console.
The same code as above
function main() { let a = fun('hello world') console.log(a) } function fun(a) { printStack() return a } function printStack() { let stack = (new Error()).stack console.log(stack) } main() Copy code
The output result is a string, as follows
Error at printStack (c:\Users\zeyu\Desktop\demo\main.js:12:16) at fun (c:\Users\zeyu\Desktop\demo\main.js:7:3) at main (c:\Users\zeyu\Desktop\demo\main.js:2:11) at Object.<anonymous> (c:\Users\zeyu\Desktop\demo\main.js:16:1) at Module._compile (node:internal/modules/cjs/loader:1095:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10) at Module.load (node:internal/modules/cjs/loader:975:32) at Function.Module._load (node:internal/modules/cjs/loader:816:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12) at node:internal/main/run_main_module:17:47 hello world Copy code
Since the result is a string, you can get the function (fun) and the calling location (c:\Users\zeyu\Desktop\demo\main.js:7:3) by splitting it by split, as shown below
function main() { let a = fun('hello world') console.log(a) } function fun(a) { printStack() return a } main() function printStack() { const callstack = new Error().stack.split("\n"); callstack.forEach((s) => { let matchArray = s.match(/at (.+?) ((.+?))/) if (!matchArray) return let name = matchArray[1] let location = matchArray[2] console.log(name, location) }) } Copy code
The output results are as follows (because it is a Node environment, some modules will be output)
printStack c:\Users\zeyu\Desktop\demo\main.js:14:21 fun c:\Users\zeyu\Desktop\demo\main.js:7:3 main c:\Users\zeyu\Desktop\demo\main.js:2:11 Object.<anonymous> c:\Users\zeyu\Desktop\demo\main.js:11:1 Module._compile node:internal/modules/cjs/loader:1095:14 Object.Module._extensions..js node:internal/modules/cjs/loader:1124:10 Module.load node:internal/modules/cjs/loader:975:32 Function.Module._load node:internal/modules/cjs/loader:816:12 Function.executeUserEntryPoint [as runMain] node:internal/modules/run_main:79:12 hello world Copy code
summary
If you want to output the call stack as a debugging phase, console.trace() is certainly the best choice, but it can only be displayed on the console and cannot be used in the running environment
The premise of using arguments.callee.caller is in non strict mode, so if you want to use it, you need to delete "use strict"; Code, but it can directly print out the complete function and the parameters passed in by the call.
new Error().stack It is equivalent to active error reporting. Since the error reporting will automatically print the call information where the error is reported, it can accurately locate the function name, code lines and columns of the code, and it is preferred for the subsequent location of the code.