16

Lately I've become a huge fan of the function.name property.

For example, I've written a function for extending prototypes.

It works in the way of..

Array.give(
    function forEach() { ... }
);

..which would then let you do..

['a', 'b', 'c'].forEach(function () { ... });

This code works great in Chrome, Safari, Firefox, and Opera, but not in IE.

After just a small bit of digging, I realized that to the give function, function.name was just returning undefined, where as in everything else it returned "forEach".

Is there an alternative way to get the name in IE, or should I just fall out of love with this wonderful property?

Brian Webster
  • 30,033
  • 48
  • 152
  • 225
McKayla
  • 6,879
  • 5
  • 36
  • 48
  • 4
    https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/name -- says *Non-standard*. – Felix Kling Aug 01 '11 at 20:05
  • 5
    What is `.give()`? And where do you use `function.name`? – Tomalak Aug 01 '11 at 20:05
  • @Felix Alright. :\ Fall out of love it is then. – McKayla Aug 01 '11 at 20:07
  • @Tomalak That doesn't matter. The question is "Is there an alternative way to get the name in IE?" exactly as I said. – McKayla Aug 01 '11 at 20:08
  • @Tomalak: I'm guessing he's extended `Object.prototype` with a `.give()` method used to extend the prototype of other types. – user113716 Aug 01 '11 at 20:09
  • 5
    If it doesn't matter then why do you use it as a sample? (@patrick I'm guessing the same, but I was interested in the implementation. Plus, I'm pretty sure that `function.name` is not necessary at all) – Tomalak Aug 01 '11 at 20:10
  • @tylermwashburn: I suppose you're trying to avoid passing a string with the function. I can understand that, but I'd guess it will be most reliable to do so. – user113716 Aug 01 '11 at 20:14
  • @patrick `Function.prototype` actually, but close. And the function does support `(property, value)`, I just prefer `(function property() {})`. It feels cleaner, makes more sense to write. I guess I'll have to switch though, because support for it is important, with a third of the market share and all. :\ – McKayla Aug 01 '11 at 20:32
  • 1
    @Tomalak To show a use case for `function.name`. How I use `function.name` inside is hardly relevant. If you're just interested in the code of it, I'd be happy to give it to you, but I assume that when people ask questions in the comments, it's to help provide an answer, so if I can keep people from giving irrelevant answers by not answering their question, then I will. And no, it's not necessary, it's just very convenient. – McKayla Aug 01 '11 at 20:36
  • @tylermwashburn: Ah yes, `Function.prototype` would make more sense since you're just interested in constructors. I suppose you could still check for the `name` property, and use a regex if unavailable (undoubtedly you've already thought of this), but regex-ing a `func.toString()` would somehow make me a little nervous. – user113716 Aug 01 '11 at 20:39
  • @patrick If it was a big function, the execution could take too long to make it helpful. :\ You could probably assume the function name is less than 100 characters, and cut the rest of the function off, just execute it on the beginning, but even that feels dangerous. Programming for other people sucks, because you have to be able to support every single circumstance. :\ – McKayla Aug 01 '11 at 20:43
  • 1
    @tylermwashburn I think it's a good thing not to take every comment at face value. Especially if it's seemingly pointless comments from high-rep users. **a)** You might not be as clear as you think you are. **b)** Programming is at least the same amount of *"why do I do this"* as it is *"how can I do this"*. **c)** Stating your intentions is part of your job when asking a question. Downright refusal to state one's intentions is borderline rude (at least that's how I perceive it). – Tomalak Aug 01 '11 at 20:43
  • -1 don't use .name, even though I love it too. – Ryan Florence Aug 01 '11 at 20:51
  • @Tomalak I don't see why though. All I wanted was an alternative to `function.name`. That's as clear as it gets. That is absolutely all the detail needed for this question. Sorry if I came across as rude, I honestly wasn't trying to be, I've just had a long day filled with agony and disappointment, and I'm getting tired of all the shit that gets thrown at me every time I visit this website. I want to ask a question, and get an answer. That's it. I really should have 2000 reputation, but I constantly get down voted for no real reason that I can see and it gets tiring. – McKayla Aug 01 '11 at 20:55
  • @rpflo ARE YOU KIDDING ME? THAT'S NO REASON TO DOWN VOTE ME. I EVEN ASKED IF DITCHING IT WOULD BE THE BEST IDEA, AND YOU STILL DOWN VOTE ME? – McKayla Aug 01 '11 at 20:56
  • @tylermwashburn Even if it sounds lame, but nobody here gets downvoted for no reason. There are far too many users here to even develop much personal antipathy against anyone. People commenting or questioning you do invest their time into your problem, that's already worth appreciation. For example: I'm not at all into down-voting, so I rather write a comment if I get suspicious. After all, a question can only be as good as the person asking it, and sometimes just answering it uncritically does not actually help them. – Tomalak Aug 01 '11 at 21:04
  • @Tomalak Well look at this guy who just did. That is the worst reason I have ever heard. No, nobody will really hold what you say against you in the long run, I got 3 down votes all at the same time, about 20 seconds after I told J-P what's what. I can see what you're saying by needing to be critical sometimes, but I don't see why me liking function.name gives anyone a reason to slam down my OO programming style. – McKayla Aug 01 '11 at 21:09
  • 1
    @tylermwashburn Maybe there *is* some option-voting going on here (another reason I'm not too fond of down-votes at large), but that carries some meaning as well. Preventing it depends heavily on the question style. I assume a question like *"I know this is non-standard, but I use it for such-and-such and therefore deem it useful, please advise a work-around for IE or show me a viable alternative."* might even have caught up-votes only. Besides, your net rep balance for the question is still positive, so just swallow your pride. ;) – Tomalak Aug 01 '11 at 21:19
  • 1
    @tylermwashburn: Maybe you should have done a little research first. It's not exactly hidden knowledge. ;) Good preparation is part of a good question. – Tomalak Aug 01 '11 at 21:36
  • @tylermwashburn - Questions that could propagate bad JS in the wild deserve to be down-voted. I didn't down vote you personally, I'm sure you're a stand-up guy. If I'm wrong, then enough people will up vote it. Regardless, extending prototypes is generally frowned upon, and then using non-standard ways to do it is even worse. – Ryan Florence Oct 27 '11 at 04:26
  • Good thing it seems like this will be standard in ES6. :D `Function.name` is extremely useful, especially on with constructors (e.g. `myObject.constructor.name`) because it gives you the name of an object's leaf-most class in the object's inheritance tree (assuming inheritance is properly designed without using anonymous functions for constructors like I've seen people do), which is more specific than using `instanceof` to check what type of object you have because `instanceof` will evaluate against all the constructor names in an object's inheritance tree. – trusktr Oct 26 '14 at 20:50

5 Answers5

32

You can use Object.defineProperty to add support for it on IE9+

// Fix Function#name on browsers that do not support it (IE):
if (!(function f() {}).name) {
    Object.defineProperty(Function.prototype, 'name', {
        get: function() {
            var name = (this.toString().match(/^function\s*([^\s(]+)/) || [])[1];
            // For better performance only parse once, and then cache the
            // result through a new accessor for repeated access.
            Object.defineProperty(this, 'name', { value: name });
            return name;
        }
    });
}
Jürg Lehni
  • 1,677
  • 13
  • 16
  • I have changed the regular expression to work correctly in all these situations: function() {}, function () {}, function test() {}, function test () {} – Jürg Lehni Jun 12 '13 at 15:50
  • 2
    instead of \w* you might want to use \S* if function name has unicode characters – alex Nov 01 '13 at 09:38
  • @alex I wasn't aware that function names can have unicode names. Are you sure that's the case? – Jürg Lehni Dec 23 '13 at 11:47
  • 3
    function name is an Identifier, identifiers conform to 7.6 ES5 spec, which includes unicode. Also, jquery has '$' as a function name, and \w won't match it as well. – alex Dec 25 '13 at 15:27
  • good to know, thanks @alex! i've adjusted the example accordingly. – Jürg Lehni Jan 04 '14 at 16:04
  • This regular expression fails for this function: function GetAnswer(){return(42)} The accepted answer's regex works better. – Johan Levin Apr 01 '16 at 12:10
  • @JohanLevin thanks for pointing that out! I've adjusted it accordingly. – Jürg Lehni Apr 09 '16 at 01:35
  • This code also leads to a runtime exception for anonymous functions. So I defined a standard value for name like `var name = 'anonymous func; ` and put the rest of the get() code into a try-catch-handler. – John Archer Mar 02 '17 at 15:04
  • 3
    @JohnArcher I've adjusted the code to accommodate that scenario. Thanks for pointing it out! – Jürg Lehni Mar 10 '17 at 17:42
  • @JürgLehni Perfect, much cleaner than my solution, thanks! – John Archer Mar 14 '17 at 10:21
  • The polyfill doesn't support "Inferred function names", see MOZ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name – andreas Sep 06 '17 at 09:27
  • @andreas, yes, ECMAScript 2015 wasn't around when it was written. And I don't think it's possible to do something about this situation in pure JS – Jürg Lehni Nov 01 '17 at 20:44
22

You might be able to parse the function name from calling function.toString [docs]. function.name is not a standard property.

var name = func.toString().match(/^function\s*([^\s(]+)/)[1];

As the comments also say, this is not necessarily a reliable way. Imo passing an object would be easier to read and you could pass several methods at once:

Array.give({
    forEach: function() { ... },
    somethingElse: function() {...}
});
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • It'd be a pain, but it looks like it'd be possible. – McKayla Aug 01 '11 at 20:11
  • I actually already have the give method mentioned setup to support objects, and multiple function arguments. – McKayla Aug 01 '11 at 20:22
  • 1
    I seriously recommend against parsing the output of a function's `toString()` method to get the name. There are no guarantees whatsoever. – Tim Down Aug 01 '11 at 23:41
  • 2
    @Tim: That's why I said *might* ;) But anyway, the [specification](http://ecma262-5.com/ELS5_HTML.htm#Section_15.3.4.2) says: *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.* So at least per specification the `toString` method returns a function declaration as string, which means there is *some* guarantee. The white spaces can be taken care of I guess... – Felix Kling Aug 01 '11 at 23:49
  • 2
    I don't think it's that hard to reliably parse func names out of their toString representations for IE. white-space can't be a part of the function label. – Erik Reppen Aug 30 '12 at 16:46
  • Yes, please use name.trim() to get the correct function name without whitespace (or whatever replacement for it in earlier IE http://stackoverflow.com/questions/2308134/trim-in-javascript-not-working-in-ie) – ricosrealm Feb 14 '13 at 19:57
  • 1
    @ricosrealm: I updated the expression to ignore whitespace characters. – Felix Kling Feb 14 '13 at 20:02
  • 1
    @TimDown Why wouldn't it work? If no name is found between `function` and `(`, then the name can be assumed to be`""` which is the same result as with `(function() {}).name` in systems where `Function.name` *is* supported. It seems like this could work 100% of the time (though it could be slow for large functions). Are there systems where the result of `Function.toString()` does not contain `"function ...()"`? – trusktr Oct 26 '14 at 20:52
  • It wouldn't work for an arrow function, for instance. – cvkline Feb 25 '19 at 20:51
7

I think your .give() solution is a little..verbose. What's wrong with:

Array.prototype.forEach = function () { ... };

?

Really, though, you should check for such a method's existence before supplying your own:

Array.prototype.forEach = Array.prototype.forEach || function () { ... };

Since others will be led here wondering about function.name, there is a way to grab the name (obviously, it doesn't work on anonymous functions):

function getFnName(fn) {
    return (fn.toString().match(/function (.+?)\(/)||[,''])[1];
}
James
  • 109,676
  • 31
  • 162
  • 175
  • 2
    `Array.prototype.forEach || ( Array.prototype.forEach = function () { ... } );` – Tomalak Aug 01 '11 at 20:08
  • 2
    This has literally nothing to do with the actual question. -1 – McKayla Aug 01 '11 at 20:09
  • I edited it. Now it does. Actually, it was already relevant to the question since you#re asking about OVERWRITING native methods. – James Aug 01 '11 at 20:10
  • @Tomalak, we'll need some extra parenthesis since `=` has a lower precedence than `||`. – James Aug 01 '11 at 20:12
  • 1
    I never asked anything about extending the prototypes. It was simply an example of how function.name could be useful. Nothing more. The only question in the entire thing is asking about an alternative you could use in IE, meaning that that is all your answer should've covered. If I want to overwrite natives, I will, because that's my own coding style. You have no reason to object. – McKayla Aug 01 '11 at 20:14
  • 4
    @tylerwashburn, I'm just trying to help. Take your indignation elsewhere. – James Aug 01 '11 at 20:15
  • @J-P It's my question. Why don't you go elsewhere? – McKayla Aug 01 '11 at 20:16
  • @tylermwashburn, J-P: It's ok now... stop it. – Felix Kling Aug 01 '11 at 20:19
  • 1
    Note that `getFnName` is using the clever `match()` trick @J-P blogged about: http://james.padolsey.com/javascript/match-trick/ – Mathias Bynens Aug 01 '11 at 20:23
  • 3
    Tyler, I suppose there was a way to ask this question without talking about your `Object.prototype.give` augmentation and just stick to the `Fn.name` part. You kinda had it coming. (And you even knew it and said "go ahead, shoot me"...) I don't think you can expect developers to not shoot ya with something like this. :) – Paul Irish Aug 01 '11 at 20:29
  • @Paul There was a way, but I thought it would be useful to show what I was talking about, plus it bulked up the question. Longer question don't usually get negative votes, obviously this one did because *somebody* is upset that I hurt their feelings. AND WHY DOES EVERYONE ASSUME IT WAS `Object.prototype`? I'm not that stupid. It was `Function.prototype`. :( – McKayla Aug 01 '11 at 20:40
0

For those, who are in the same boat, have a look at JamesMGreene's Function.name polyfill. This looks to be a good solution.

bencergazda
  • 620
  • 8
  • 18
0

Old question, and I don't know if this works on a native IE 7 & 8 browser (I'm using the developer tools emulator), but I've given functions a name property on the constructor for gag inducing IE code before....

function sayMyName(func){
    var name=func.name || func.constructor.name
    alert(name);

}

var ieGivesMeNightTerrors=function(){
    //there there, these ugly workarounds aren't your fault
};
ieGivesMeNightTerrors.constructor.name=ieGivesMeNightTerrors;
sayMyName(myFunc);
user2782001
  • 3,380
  • 3
  • 22
  • 41