JavaScript在控制台中更改构造函数名称

3
使用构造函数工厂,我希望在控制台中使用不同的名称来命名这些构造函数,同时在记录它们的实例时也是如此。
以下是我的问题的简化示例:
// 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

在这种情况下,有没有办法显示正确的名称?

已经测试了最新版本的Chrome(67)。在这种情况下,我不想使用class特性。


1
类猫 { /.../ }。我认为,如果没有一些非常奇怪的 with(){}eval 代码,你无法影响控制台的输出。 - Jonas Wilms
我建议不要像那样将所有的构造函数都设为全局变量。全局命名空间已经非常拥挤了。 - T.J. Crowder
没有重复 https://dev59.com/q2kx5IYBdhLWcg3wCf8q,请参阅我的答案,了解其原因。 - T.J. Crowder
1
@T.J.Crowder 不用担心,我通常不会将所有内容设置为全局命名空间,这只是为了举例。;) - Tot
1个回答

5
您可能会认为具有计算属性解决方法是回答这个问题的答案,但对于您描述的构造函数情况,它不起作用(至少在Chrome或Firefox现在,在至少Firefox v66中它可以工作;它在Edge和Node.js中都可以工作,这让我感到惊讶,尽管Node.js和Chrome都使用V8 [请参见此评论]):

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.

很不幸的是,尽管Function#name现在是函数规范特性之一,但正如你所发现的那样,name并不总是JavaScript引擎内部在堆栈跟踪等方面使用的名称(至少目前如此; 希望随着name的成熟而改变; 它只是在ES2015中添加的)。
如果你真的需要这样做,这是极少数需要生成和执行动态代码(例如new Function)的地方之一。
var Constructor = new Function("return function " + name + "() { };")();

以下代码可以动态地使用 name 代替 nameGoesHere 实现同样的功能:

var Constructor = (function() {
    return function nameGoesHere() { };
})();
new Function 是用于创建我们立即执行的外部函数。
实时示例(查看实际控制台输出):

// 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) ;

显然,这假定你不是从不可信的来源获取name

1
@JonasW. - 如果name不来自一个不可信的来源,那么在生产环境中使用它是完全可以的。 - T.J. Crowder
1
@JonasW. - 创建构造函数后,构造函数本身不会影响性能。以这种方式创建构造函数的过程比普通方式慢一些(当然,有更多的开销),但由于可能只有几十个这样的构造函数——最多只有几千个——所以并不重要。 - T.J. Crowder
1
糟糕。我曾经以为这个可以工作,但正如你所说,检查器名称是内部派生的,随时可能会更改。 - Bergi
1
@Tot - 我当然不能反对避免使用动态代码评估。但是仅供参考,上述内容并不繁重或复杂。我们经常做更繁重和更复杂的事情。这是字符串拼接、解析、创建函数和调用函数,这些对于JavaScript引擎或者阅读代码的人来说都不是挑战。显然,如果将来只需设置“name”就能工作,那将是很好的。 - T.J. Crowder
1
@leoschet - 谢谢!非常有趣。我在手边的 Node.js v11.10.1 中进行了测试,与你所说的一样有效。但是在 Chrome v74 中仍然无法使用。奇怪的是,Node.js v11.10.1 中的 V8 版本是 v7.0.276.38-node.17,而 Chrome v74 中的版本应该是 v7.4.x。显然,这在一年前不起作用,那时的版本为 v7.0 之前。我想知道为什么 Chrome 中的 V8 与 Node.js 中的 V8 做得不同... 可能与 console 实现有关... - T.J. Crowder
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接