3

Using a factory of constructors, I would like those constructors to have different names in the console, also when logging instances of them.

Here is a simplified example of my problem :

// Constructor factory //
function type(name, prototype) {
    function Constructor() {}
    Constructor.name ; // "Constructor"
    // Constructor.name = name  won't work properly.
    Object.defineProperty(Constructor, 'name', { value:name }) ;
    Constructor.prototype = prototype ;

    window[name] = Constructor ;
    return Constructor ;
}

// Creating constructor and instance //
type('Cat', { name:"", paws:4 }) ;
var chat = new Cat ;

// Tests //
Cat.name ; // "Cat"    -> Correct name
Cat ; // Constructor() { ... }     -> Incorrect name
chat ; // Constructor {name:"", paws:4}     -> Incorrect name

Is there any way to display the right name in this case ?

Tested with the latest version of Chrome (67). In this case I don't want to use class feature.

4
  • 1
    class Cat { /*...*/ } . And i don't think that you can influence the consoles output without some very strange with(){} or eval code. Commented Jun 10, 2018 at 11:50
  • I'd recommend not making all of your constructor functions globals like that. The global namespace is already really, really crowded. Commented Jun 10, 2018 at 11:59
  • Not a duplicate of stackoverflow.com/questions/9479046/…, see my answer for why not. Commented Jun 10, 2018 at 13:37
  • 1
    @T.J.Crowder Don't worry, I don't usually set all to the global namespace, this was just for the example. ;) Commented Jun 10, 2018 at 17:00

1 Answer 1

5

You'd be tempted to think the object with computed property workaround answer to this question would work, but it doesn't work for the constructor scenario you describe (at least, at the moment, in Chrome or Firefox it does, now, in at least Firefox v66; it does in Edge and, surprisingly to me, in Node.js despite the fact Node.js and Chrome both use V8 [see this comment]):

const dynamicName = "foo" + Math.floor(Math.random() * 1000);
const obj = {
  [dynamicName]: function() {
  }
};
const f = obj[dynamicName];
const inst = new f();
console.log(f.name);	// works
console.log(f);				// nope (on Chrome)
console.log(inst);		// nope (on Chrome)
Look in the real console.

Unfortunately, even though Function#name is now a specified feature of functions, as you've found, that name isn't always what the internals of the JavaScript engines use in stack traces and such (yet; hopefully this changes as name matures; it was only added in ES2015).

If you really need to do this, it's one of the very few places where you'd reach for generating and executing dynamic code, such as new Function:

var Constructor = new Function("return function " + name + "() { };")();

That does the following, but dynamically using name instead of nameGoesHere:

var Constructor = (function() {
    return function nameGoesHere() { };
})();

The new Function is what creates the outer function we immediately execute.

Live Example (look in the real console for output):

// Constructor factory //
function type(name, prototype) {
    var Constructor = new Function("return function " + name + "() { };")();

    window[name] = Constructor ;
    return Constructor ;
}

// Creating constructor and instance //
type('Cat', { name:"", paws:4 }) ;
var chat = new Cat ;

// Tests //
console.log(Cat.name) ;
console.log(Cat) ;
console.log(chat) ;

Obviously, this assumes you aren't getting name from an untrusted source.

Sign up to request clarification or add additional context in comments.

16 Comments

@JonasW. - It's absolutely fine to use this in production, if name doesn't come from an untrusted source.
@JonasW. - There's no performance impact on the constructor function once it's created. The act of creating the constructor function this way is a bit slower than normal ways (of course, more overhead), but as there presumably are only a couple of dozen of these -- a couple of thousand at most -- it doesn't matter.
Drat. I could have sworn this worked once, but as you say the inspector name is derived internally and this can change any time.
@Tot - I certainly can't argue against avoiding using dynamic code evaluation. :-) But just FWIW, the above isn't heavy or complicated. We do things that are heavier and more complicated all the time. This is string concatenation, parsing, creating a function, and calling it, none of which is remotely a challenge for the JavaScript engine or, one hopes, the person reading the code. Obviously, it'll be nice if just setting name works someday, though...
@leoschet - Thanks! That's very interesting. I tested it in Node.js v11.10.1 (what I had handy) and, as you said, it works. But it still doesn't work in Chrome v74. The weird thing about that is that the version of V8 in Node.js v11.10.1 is v7.0.276.38-node.17 and the version in Chrome v74 would be v7.4.something. And clearly this wasn't working in Chrome a year ago, which would have been before v7.0. I wonder why V8 in Chrome is doing this differently from V8 in Node.js... Must have to do with console implementation...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.