16

My debugging work in IE ended today by finding that constructor.name is undefined.

I created the following simple code that reproduces the issue:

({}).constructor.name === undefined // => true

Is there any workaround to make this work?

Maybe overriding somehow the prototype?

If possible, I don't want to change the syntax, because the change would be major.

JSFIDDLE

HerbalMart
  • 1,669
  • 3
  • 27
  • 50
Ionică Bizău
  • 109,027
  • 88
  • 289
  • 474
  • if you're minifying your code then `constructor.name` can likely end up being something like `n` instead of the object name you expect. So even without IE issues be very careful if you expect it to be a certain value! – Simon_Weaver Mar 08 '17 at 00:07

5 Answers5

15

From matt.scharley.me

/**
 * Hack in support for Function.name for browsers that don't support it.
 * IE, I'm looking at you.
**/
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
    Object.defineProperty(Function.prototype, 'name', {
        get: function() {
            var funcNameRegex = /function\s([^(]{1,})\(/;
            var results = (funcNameRegex).exec((this).toString());
            return (results && results.length > 1) ? results[1].trim() : "";
        },
        set: function(value) {}
    });
}
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
Olivier
  • 151
  • 1
  • 2
  • Note that is will not work for `var TestFunction1 = function() {}; console.log('TestFunction1.name', TestFunction1.name);`. – Ini Feb 18 '20 at 09:33
12

The problem is simply that the name property of function objects is not supported in Internet Explorer. The property is non-standard (up until ECMAScript 6, at least) so it's not altogether surprising.

There isn't a completely reliable workaround so I would suggest trying to do without it if possible. However, you may be able to extract the name from the string representation of the function. Here a couple of links that deal with this that I got from a quick search:

Update

From the comments, it turns out that the goal of the question author is to test whether a variable is a reference to a plain object create by the Object constructor. A reliable way of doing this for a variable a is

Object.prototype.toString.call(a) == "[object Object]"

For more information I recommend the following page written by Angus Croll:

http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 1
    What about doing something like `Object.prototype.constructor.name = "Object"`? Would it solve the problem? – Ionică Bizău Aug 05 '14 at 14:07
  • 1
    @IonicăBizău: You can certainly do that if you find it helpful, although I would suggest picking a different property name in case the `name` property is non-writable in browsers that support `name`. – Tim Down Aug 05 '14 at 14:09
  • @TimDown: The `function.name` property is non-writable (as explained in the Mozilla docs). But why do you think it should be avoided if it is non-writable? – Gerd Wagner Aug 05 '14 at 14:20
  • @gwag: I prefer to leave native properties and methods alone rather than try to shim them but in this case I suppose there's not much sense in that. – Tim Down Aug 05 '14 at 14:25
  • @TimDown `a.constructor === Object` seems to work fine, but is it recommended? – Ionică Bizău Aug 05 '14 at 14:26
  • @IonicăBizău: It's not going to cause any problems unless you have code that changes the `constructor` property elsewhere or you're testing objects that come from an iframe or different window. I can't really make any recommendations without knowing more about what you're trying to achieve. – Tim Down Aug 05 '14 at 14:34
  • @TimDown I just want to verify if a variable is an `Object`. Until now, I was doing `constructor.name === "Object"`. `typeof {}` returns `"object"`, but it returns the same for arrays. So, are there any better ways for verifying if a variable is object? – Ionică Bizău Aug 05 '14 at 14:38
  • @IonicăBizău: `Object.prototype.toString.call(a) == "[object Object]"` is pretty safe. Here's a good article on this subject: http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ – Tim Down Aug 05 '14 at 14:49
  • @TimDown Maybe you can update your answer providing this information. – Ionică Bizău Aug 05 '14 at 14:59
0

Perhaps this example clears up some confusion.

var number = 10;        //A global variable. Part of the global window object.
//window.number = 10;   //Does exactly the same as the line above.


var customer = {
    number: 20,
    speak: function () {
        if (this === window) {
            alert('I am being called by go() ' + this.number);      //this.number points to the global variable number
        } else {
            alert('I am being called by customer.speak ' + this.number);  //this.number points to the customer member number
        }
    }
}

var go = customer.speak;  //Go now points directly to the function called "speak" as if it is not a part of the customer object. The this parameter of speak will be the global window object;

go();
customer.speak();         //Now the speak function is called a part of the customer object. The this parameter of speak will be the customer object
HerbalMart
  • 1,669
  • 3
  • 27
  • 50
0

This is an improvisation from Oliver's answer.

Instead of using regex, this method will parse the string once and save it to the scope to avoid performance problem if it's called more than once.

if(Function.prototype.name === undefined){
    Object.defineProperty(Function.prototype, 'name', {
        get:function(){
            if(!this.__name)
                this.__name = this.toString().split('(', 1)[0].split(' ')[1];
            return this.__name;
        }
    });
}
StefansArya
  • 2,802
  • 3
  • 24
  • 25
0

I wrote this for my needs (acknowledging it may not be full-proof):

if (!Object.constructor.prototype.hasOwnProperty('name')) {
    Object.defineProperty(Object.constructor.prototype, 'name', {
        get: function () {
            return this.toString().trim().replace(/^\S+\s+(\w+)[\S\s]+$/, '$1');
        }
    })
}

The regex being performed in the method is making an assumption that the 2nd non-whitespace group of characters IS the name of the constructor. This is a viable solution for the code that I'm working with, while this may not be full-coverage for all others' needs.

Erutan409
  • 730
  • 10
  • 21