29

I need the current function name as a string to log to our log facility. But arguments.callee.name only works in loose mode. How to get the function name under "use strict"?

exebook
  • 32,014
  • 33
  • 141
  • 226
  • 2
    If you are able to modify the function's body to add logging code, can't you just hard-code the function name? What advantage would you have if you could read it from a property? – nnnnnn Jul 18 '16 at 11:36
  • 9
    @nnnnnn someone keeps renaming functions but forgets to update the log line. – exebook Jul 18 '16 at 11:44
  • did you find a nice solution for it? creating an Error object may have a performance impact, so, I'm keep looking the nice way to get the function name. – Kostanos May 11 '22 at 05:14
  • @Kostanos not directly, but a workaround for that particular case what was to type `log('^FUNAME^')` instead and then use my own tiny parser on production that would replace ^FUNAME^ with a current function name which is found after the preceeding word `function`. – exebook May 22 '22 at 16:46

7 Answers7

41

For logging/debugging purposes, you can create a new Error object in the logger and inspect its .stack property, e.g.

function logIt(message) {
    var stack = new Error().stack,
        caller = stack.split('\n')[2].trim();
    console.log(caller + ":" + message);
}

function a(b) {
    b()
}

a(function xyz() {
    logIt('hello');
});
georg
  • 211,518
  • 52
  • 313
  • 390
  • Your answer is a little bit complicated, but it shows the idea. – exebook Jul 18 '16 at 12:04
  • A great answer @georg. Note that ``[2]`` is relative to this example only, if you have for example a stack containing three parent functions in this case we must change **2** to **3** – ismnoiet Jul 18 '16 at 12:40
  • 5
    @hamism: `stack[2]` is always the function that called the logger, no matter how deep the stack is. – georg Jul 18 '16 at 17:59
  • Now this is a really nice idea :) – Loves2Develop Jan 25 '18 at 15:22
  • 3
    It is a clever solution for logging/debugging. To anyone else considering using this in production code though (in my case, for event tracking), be warned: *"This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future."* (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack) – evanrmurphy Oct 08 '18 at 01:03
6

You can bind function as its context then you can access its name via this.nameproperty:

function x(){
  console.log(this.name);
}
x.bind(x)();
LJ Wadowski
  • 6,424
  • 11
  • 43
  • 76
3

After little research here is a good solution :

function getFnName(fn) {
  var f = typeof fn == 'function';
  var s = f && ((fn.name && ['', fn.name]) || fn.toString().match(/function ([^\(]+)/));
  return (!f && 'not a function') || (s && s[1] || 'anonymous');
}



function test(){
    console.log(getFnName(this));
}

test  = test.bind(test);

test(); // 'test'

Source : https://gist.github.com/dfkaye/6384439

ismnoiet
  • 4,129
  • 24
  • 30
  • 6
    But that doesn't work from within the function unless you already have a reference to the function. OP asked for a replacement for `arguments.callee.name`... – nnnnnn Jul 18 '16 at 11:32
3

Building on @georg solution, this one returns just the function name. Note though that it may fail if called from an anonymous function

function getFncName() {
    const stackLine = (new Error())!.stack!.split('\n')[2].trim()
    const fncName = stackLine.match(/at Object.([^ ]+)/)?.[1]
    return fncName
}

function Foo() {
    console.log(getFncName()) // prints 'Foo'
}
bdombro
  • 1,251
  • 11
  • 17
0

A simple solution to dynamically retrieve function names [like magic variables] is the use of scoped variables, and the Function.name property.

{
  function foo() {
    alert (a.name);
  }; let a = foo
}
{
  function foo2() {
    alert(a.name)
  }; let a = foo2
};
foo();//logs foo
foo2();//logs foo2

Note: Nested functions cease to be source elements, and are hence not hoisted. Also, this technique cannot work with anonymous functions.

Eazicoding
  • 191
  • 9
0

If (like me) you want to define this elsewhere and call it generically, you can store the code as a string somewhere global or import it, then eval() it wherever to access the current function name. (Using eval keeps the context at the point of invocation.)

There's gotta be a way to do this without using a string, but whatever.

SomeObject.whatFunc =
  'const s = new Error().stack;' +
  "const stackLine = new Error().stack.split('\\n')[2].trim();" +
  'const fncName = stackLine.match(/(?<=at )(.*)(?= \\()/gm)[0];' +
  'console.log(fncName);'

// Whereever you want the func name
function countBananas('weeee') {
  eval(SomeObject.whatFunc)
  // blah blah blah
}

countBananas() // logs 'countBananas'
MetaWhirledPeas
  • 394
  • 1
  • 4
0

just an update to get the full name :

function logIt(message) {
    var stack = new Error().stack,

    //  update is on this line
    caller = stack.split('\n')[2].trim().split(/\s+/)[1];

    console.log(caller.trim().split(/\s+/)[1];);
}

function a(b) {
  b()
}

a(function xyz() {
  logIt('hello');
});