获取ES6类实例的类名称


156

是否有任何“和谐”方式从ES6类实例获取类名?以外

someClassInstance.constructor.name

目前,我指望Traceur的实现。而且,Babel似乎有Function.nameTracefill没有的polyfill。

综上所述:ES6 / ES2015 / Harmony中没有其他方法,并且ES中没有ATM。

它可以为未缩小的服务器端应用程序提供有用的模式,但是在旨在用于浏览器/桌面/移动设备的应用程序中是不需要的。

巴别用途core-js来填充工具Function.name,应当手动Traceur和打字稿应用适当装载。


2
我遇到了同样的问题;对于Traceur来说,唯一的解决方案是解析实际的类代码本身以提取名称,我认为这不算是和谐的。我只是吞下药丸,然后换了巴别塔。Traceur的发展似乎停滞不前,并且许多ES6功能的实现不佳。如前所述,instance.constructor.nameclass.name在正确的ES6中返回类名。
安德鲁·奥德里

似乎是唯一的方法。
Frederik Krautwald

这在ES6标准中吗?
drudru

12
值得一提的是,someClassInstance.constructor.name如果您丑化了您的代码,它将被弄得一团糟。
JamesB 2015年

stackoverflow.com/questions/2648293/…可能要看一下,应该可以使用.constructor
Florrie

Answers:


204

someClassInstance.constructor.name正是这样做的正确方法。编译器可能不支持此功能,但这是规范中的标准方法。(name通过ClassDeclaration产生式声明的函数的属性在14.5.15的步骤6中设置。)


2
那就是我所担心的。您知道为此使用任何合理的polyfill吗?我试图弄清楚Babel是如何做到的,但收效甚微。
Estus Flask 2015年

我真的不知道您对语言功能(类)使用polyfill是什么意思。
Domenic 2015年

我的意思是为builder.name的polyfill。Babel似乎已经实现了它,但是我没有成功理解它是如何实现的。
Estus Flask 2015年

2
@estus someClassInstance.constructor是一个函数。所有函数都有一个name设置为其名称的属性。这就是为什么babel不需要做任何事情。请参阅developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
埃斯特班

2
@Esteban看来Babel一直在推广core-js的polyfill(包括用于Function.name的polyfill),这就是为什么某些Babel构建可能在所有浏览器中都可以立即使用的原因。
Estus Flask

52

如@Domenic所说,使用someClassInstance.constructor.name。@Esteban在评论中提到

someClassInstance.constructor是一个功能。所有函数都有一个name属性...

因此,要静态访问类名,请执行以下操作(这适用于我的Babel版本BTW。根据@Domenic上的注释,您的行驶里程可能会有所不同)。

class SomeClass {
  constructor() {}
}

var someClassInstance = new SomeClass();
someClassInstance.constructor.name;      // === 'SomeClass'
SomeClass.name                           // === 'SomeClass'

更新资料

Babel很好,但是丑化/缩小确实导致了我麻烦。我正在做一个游戏,并且正在创建一个池中Sprite资源的哈希(其中键是函数名)。缩小后,每个函数/类都被命名为t。这杀死了哈希。我正在Gulp这个项目中使用,阅读完gulp-uglify文档后,我发现有一个参数可以防止发生这种局部变量/函数名称错误的情况。所以,在我的gulpfile中,我改变了

.pipe($.uglify()).pipe($.uglify({ mangle: false }))

这里要权衡性能与可读性。不修改名称将导致(略微)较大的构建文件(更多的网络资源),并可能导致代码执行速度变慢(需要引用-可能是BS)。另一方面,如果保持不变,则必须getClassName在每个ES6类上手动定义-在静态和实例级别。不用了,谢谢!

更新资料

在评论中进行讨论之后,似乎避开.name约定以支持定义这些功能是一个很好的范例。它只需要几行代码,就可以完全缩小代码的通用性(如果在库中使用)。因此,我想我改变主意了,将getClassName在课堂上进行手动定义。谢谢@estus!。无论如何,与直接变量访问相比,Getter / Setters通常是一个好主意,尤其是在基于客户端的应用程序中。

class SomeClass {
  constructor() {}
  static getClassName(){ return 'SomeClass'; }
  getClassName(){ return SomeClass.getClassName(); }
}
var someClassInstance = new SomeClass();
someClassInstance.constructor.getClassName();      // === 'SomeClass' (static fn)
someClassInstance.getClassName();                  // === 'SomeClass' (instance fn)
SomeClass.getClassName()                           // === 'SomeClass' (static fn)

3
完全禁用重整不是一个好主意,因为重整会极大地促进缩小。首先不要在客户端代码中使用此主题,也不是一个好主意,但是可以使用reservedUglify选项保护类免于篡改(可以使用regexp或任何其他方式获取类列表)。
Estus Flask

非常真实 有一个更大文件大小的权衡。看起来这是您可以使用RegEx防止仅对选定项进行损坏的方式。您是什么意思“在客户端代码中使用该主题也不是一个好主意”?在某些情况下会带来安全风险吗?
James L.

1
不,刚才已经说过了。最小化客户端JS是正常的,因此已经知道此模式会给应用程序带来问题。类字符串标识符的额外一行代码而不是整齐的name模式可能是双赢的。可以将相同的内容应用于Node,但程度较小(例如,混淆的Electron应用程序)。根据经验,我将依赖name服务器代码,而不是浏览器代码和公共库。
Estus Flask

好的,因此,建议您手动定义2个getClassName函数(静态和实例),以避开混乱的地狱并允许完全缩小(没有烦人的RegEx)。关于库的这一点很有意义。很有道理。对我来说,我的项目是一个自制的小型Cordova应用程序,因此这些并不是真正的问题。除此之外,我还可以看到这些问题正在上升。感谢您的讨论!如果您认为该帖子有任何改进,请随时对其进行编辑。
James L.

1
是的,我最初使用nameDRYer代码从类名称中提取使用类(服务,插件等)的实体的名称,但最后我发现它与静态prop(id_name)显式地复制了实体只是最可靠的方法。一个很好的选择是不要在意类名,使用类本身(函数对象)作为映射到此类的实体的标识符,并import在需要时使用它(Angular 2 DI使用的一种方法)。
Estus Flask

8

直接从课程中获取课程名称

先前的答案解释说,这种方法someClassInstance.constructor.name很好用,但是如果您需要以编程方式将类名转换为字符串,并且不想为此创建实例,请记住:

typeof YourClass === "function"

而且,由于每个函数都有一个name属性,因此使用类名获取字符串的另一种不错的方法是:

YourClass.name

接下来的例子很好地说明了为什么这样做很有用。

加载网页组件

正如MDN文档所教导的,这是您加载Web组件的方式:

customElements.define("your-component", YourComponent);

从那里YourComponent延伸的班级在哪里HTMLElement?由于在组件标签本身之后命名组件的类是一种好习惯,因此最好编写一个辅助函数,所有组件都可以使用该函数来注册自己。所以这是该函数:

function registerComponent(componentClass) {
    const componentName = upperCamelCaseToSnakeCase(componentClass.name);
    customElements.define(componentName, componentClass);
}

因此,您需要做的就是:

registerComponent(YourComponent);

这样做很好,因为它比自己编写component标签要容易出错。总结一下,这是upperCamelCaseToSnakeCase()函数:

// converts `YourString` into `your-string`
function upperCamelCaseToSnakeCase(value) {
    return value
        // first char to lower case
        .replace(/^([A-Z])/, $1 => $1.toLowerCase())
        // following upper chars get preceded with a dash
        .replace(/([A-Z])/g, $1 => "-" + $1.toLowerCase());
}

谢谢。该示例是客户端。正如已经提到的,在浏览器中使用函数名称存在一些问题。几乎所有浏览器的代码段都将被压缩,但这将破坏依赖于函数名的代码。
Estus Flask

是的,您完全正确。为了使这种方法起作用,必须将Minifier配置为不触摸类名。
Lucio Paiva

1

进行通天译本(缩小前)

如果您将Babel与结合使用@babel/preset-env,则可以保留类定义,而无需将其转换为函数(这会删除constructor属性)

您可以在此配置中删除与此配置之间的旧浏览器兼容性babel.config / babelrc

{
  "presets": [
    ["@babel/preset-env", {"targets": {"browsers": ["> 2%"]}}]
  ]
}

有关更多信息targetshttps : //babeljs.io/docs/en/babel-preset-env#targets

为了缩小通天粉(移植后)

看来目前没有简单的解决方案...我们需要研究如何处理排除问题。


您能否进一步说明这对缩小有何帮助?class Foo {}将被缩小为class a{}任何目标。Foo缩小的源代码中没有任何单词。
Estus Flask

老实说,我没有深入研究文档,而且事实证明它对我使用此配置有所帮助...我正在将ECSY用于babel转译的项目中,并且它需要此参数来获取有效的类名:github.com/MozillaReality/ecsy/issues/ 119
如果不是

我懂了。这非常针对您处理的代码。例如,可以为ES模块和ES6保留名称,因为export class Foo{}无法进一步有效地使其丑化,但是在其他地方可能有所不同,如果不知道在构建时特定代码段会发生什么情况,我们就无法确切知道。无论如何,这自2015年以来就没有改变过。对于某些编译配置和代码,这始终是可能的。而且我会说这种可能性仍然太脆弱,无法使用类名进行应用程序逻辑处理。意外更改源代码后,您依赖的类名称可能会变成垃圾
Estus Flask

1
好的,我通过查看代码发现了什么。我的解决方案将类的转换修复为函数。因此在缩小之前有帮助。但是没有缩小问题。我必须继续进行挖掘,因为我无法理解我使用的所有代码constructor.name仍如何在缩小版本中工作……不合逻辑:/
如果
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.