3

Since ES5 doesn't support Function#name. I was looking for a way to emulate that functionality. While a lot of people recommend using Function#toString, others strongly advised against it.

So what are the risks of using the below code to get the name of a function?

if (!Object.hasOwnProperty(Function.prototype, "name")) {
  Object.defineProperty(Function.prototype, "name", {
    configurable: false,
    enumerable: true,
    get: function() {
      var result = /function\s+([^\s(]+)/.exec(this.toString());
      return result ? result[1] : "";
    }
  });
}

Since ES5 doesn't support arrow functions, I don't really see when the where the risk lies.

nick zoum
  • 7,216
  • 7
  • 36
  • 80
  • 1
    You may wish to [change the regex](https://stackoverflow.com/a/9337047/) to account for the more unusual possible function names (most prominently, `$`). Maybe [`function\s+([^(\s]+) *\(`](https://regex101.com/r/SQ2gXR/1), since the Javascript syntax can be assumed to already be valid – CertainPerformance Oct 12 '19 at 08:33
  • @CertainPerformance Would something like`function\s+([^\s(]+)` work? My thinking is that any function that is being checked will already have a valid name, therefore the name's end should be at the index where a space (space, tab, new line ...) or `(` is found. – nick zoum Oct 12 '19 at 08:42
  • Sure, the trailing part of the match isn't necessary, it just feels a bit more solid – CertainPerformance Oct 12 '19 at 08:45
  • Just be sure to handle all (appropriate) cases from the [Examples](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/tostring) table. Particularly, `func.toString` doesn't always start with "function". – georg Oct 12 '19 at 08:52
  • 1
    @georg It seems to me all ES5 compatible examples start with `function` – nick zoum Oct 12 '19 at 09:11
  • What do you need the name for? Because these names are completely unreliable in real code – Thomas Oct 12 '19 at 09:52
  • @Thomas In what way are they unreliable? Is it because two or more functions can have the same name under different scopes? – nick zoum Oct 12 '19 at 09:54
  • For example, I use to minify my production builds -> all names are gone/changed to single characters. Then using currying, debouncing, memoization, ... and the names are gone, because it's not the same function anymore. I also had in mind that `Function#bind` would return a function without name; a quick test showed that it just prepends `"bound "+name` but I'd have to check wether that behaviour is defined in the spec. So imo. the names are nice to have when debugging, but in production you can not rely on them. – Thomas Oct 12 '19 at 10:02
  • @Thomas I am mostly planning on using it as `({"Date":()=>{},"Array":()=>{}...}[item.constructor.name])();` – nick zoum Oct 12 '19 at 10:06
  • As long as you're only dealing with the built in classes. But I'd use a `Map` and reference `map.get(item.constructor)` instead of its name. – Thomas Oct 12 '19 at 10:19

1 Answers1

1

As the ECMAScript 5.1 specification says, the toString method returns a string that has the syntax of FunctionDeclaration:

Function.prototype.toString ( )

An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. Note in particular that the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.

The toString function is not generic; it throws a TypeError exception if its this value is not a Function object. Therefore, it cannot be transferred to other kinds of objects for use as a method.

FunctionDeclaration has the following syntax:

FunctionDeclaration :

function Identifier ( FormalParameterListopt ) { FunctionBody }

FormalParameterList :

Identifier
FormalParameterList , Identifier

And Identifier defined like:

Identifier ::

IdentifierName but not ReservedWord

IdentifierName ::

IdentifierStart
IdentifierName IdentifierPart

IdentifierStart ::

UnicodeLetter
$
_
\ UnicodeEscapeSequence

IdentifierPart ::

IdentifierStart
UnicodeCombiningMark
UnicodeDigit
UnicodeConnectorPunctuation

Conclusion

Although it isn't a beautiful way to get the function name (but the only way in ES5), if you make it parse all of the possibilities listed above, it can work safely in ES5.

But the ES6 standard modified the specifications for .toString(), which implies more possible syntaxes, making it unsafe to use this method in it.

So, use this method only in versions prior to ES6.

FZs
  • 16,581
  • 13
  • 41
  • 50
  • Not sure if you are saying that my code will suffice, or if I need to alter it. If its the latter, could you give an example of a function declaration that would cause a problem? – nick zoum Oct 12 '19 at 09:44
  • @nickzoum Your code is OK now; but I've answered before your edit, according to [CertainPerformance's comment](https://stackoverflow.com/questions/58352293/what-are-the-risks-of-using-tostring-to-get-the-name-of-a-function/58352725?noredirect=1#comment103058616_58352293) – FZs Oct 12 '19 at 10:13