获取 ES6 类实例的类名

IT技术 javascript ecmascript-6 traceur
2021-01-14 16:26:38

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

someClassInstance.constructor.name

目前我指望 Traceur 实现。似乎 Babel 有一个Function.namepolyfill 而 Traceur 没有。

总而言之:在 ES6/ES2015/Harmony 中没有其他方式,在 ES.Next 中没有预期的 ATM。

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

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

4个回答

someClassInstance.constructor.name正是这样做的正确方法。转译器可能不支持这一点,但它是符合规范的标准方式。name通过 ClassDeclaration 产生式声明的函数属性在14.5.15 的第 6 步中设置。)

这就是我害怕的。你知道任何合理的 polyfill 吗?我试图弄清楚 Babel 是如何做到这一点的,但收效甚微。
2021-03-21 16:26:38
@Esteban Babel 似乎一直在推广 core-js polyfills(包括 Function.name 的 polyfill),这就是为什么某些 Babel 构建可以在所有浏览器中开箱即用的原因。
2021-03-29 16:26:38
我的意思是constructor.name 的polyfill。Babel 似乎实现了它,但我没有成功理解它是如何做到的。
2021-03-30 16:26:38
我真的不知道你所说的语言特性(类)的 polyfill 是什么意思。
2021-04-04 16:26:38
@estussomeClassInstance.constructor是一个函数。所有函数都有一个name设置为其名称属性。这就是为什么 babel 不需要做任何事情的原因。请参阅developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-04-12 16:26:38

正如@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 }))

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

更新

在评论中的讨论之后,似乎避免.name约定而支持定义这些函数是一个很好的范例。它只需要几行代码,并允许您的代码完全缩小和通用(如果在库中使用)。所以我想我会改变主意并手动定义getClassName我的类。谢谢@estus!. 无论如何,与直接变量访问相比,Getter/Setter 通常是一个好主意,尤其是在基于客户端的应用程序中。

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)
是的,我最初使用nameDRYer 代码从类名中提取使用类(服务、插件等)的实体的名称,但最后我发现它使用静态道具 ( id, _name)显式复制它只是最可靠的方法。一个好的选择是不关心类名,使用类本身(函数对象)作为映射到这个类的实体的标识符,并import在任何需要的地方使用(Angular 2 DI 使用的一种方法)。
2021-03-19 16:26:38
非常真实。有一个更大的文件大小的权衡。看起来这就是您可以如何使用 RegEx 来防止仅对选定项目进行修改“在客户端代码中使用该主题也不是一个好主意”是什么意思?在某些情况下是否会带来安全风险?
2021-03-21 16:26:38
好的,所以您建议手动定义 2 个 getClassName 函数(静态和实例)来绕过重整地狱并允许完全缩小(没有烦人的 RegEx)。关于图书馆的这一点很有意义。很有道理。对我来说,我的项目是一个自制的小型 Cordova 应用程序,所以这些都不是问题。除此之外的任何事情我都可以看到这些问题正在上升。感谢讨论!如果您能想到对帖子的任何改进,请随时对其进行编辑。
2021-03-23 16:26:38
完全禁用 mangling 不是一个好主意,因为 mangling 对缩小有很大贡献。首先在客户端代码中使用主题并不是一个好主意,但是可以使用reservedUglify 选项来保护类免于修改(可以使用正则表达式或其他方式获取类列表)。
2021-03-30 16:26:38
不,就是已经说过的。客户端JS被压缩是正常的,所以这种模式会导致应用程序出现问题是众所周知的。为类字符串标识符增加一行代码而不是整洁的name模式可能只是双赢。同样的事情可能适用于 Node,但程度较小(例如混淆的 Electron 应用程序)。根据经验,我会依赖name于服务器代码,而不是浏览器代码和公共库。
2021-04-08 16:26:38

直接从类中获取类名

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

typeof YourClass === "function"

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

YourClass.name

下面是一个很好的例子,说明为什么这很有用。

加载 Web 组件

正如MDN 文档告诉我们的那样,这是加载 Web 组件的方式:

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

从哪里YourComponent扩展的类HTMLElement由于在组件标签本身之后命名组件的类被认为是一种很好的做法,因此最好编写一个辅助函数,您的所有组件都可以使用它来注册自己。所以这是那个函数:

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

所以你需要做的就是:

registerComponent(YourComponent);

这很好,因为它比自己编写组件标签更不容易出错。总结一下,这是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());
}
谢谢。该示例是客户端。正如已经提到的,在浏览器中使用函数名称存在一些问题。几乎每个浏览器的代码片段都应该被缩小,但这会破坏依赖于函数名称的代码。
2021-04-03 16:26:38
是的,你完全正确。要使这种方法起作用,必须将缩小器配置为不接触类名。
2021-04-04 16:26:38

用于 babel 转译(缩小前)

如果您使用 Babel with @babel/preset-env,则可以保留类定义而不将它们转换为函数(这会删除constructor属性)

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

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

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

用于 babel 缩小(转译后)

看起来现在没有简单的解决方案......我们需要看看重整排除。

我知道了。这与您处理的代码非常具体。例如,ES module和 ES6 的名称可能会被保留,因为export class Foo{}无法进一步有效地丑化,但这在其他地方可能有所不同,如果不知道在构建时特定代码片段会发生什么,我们无法确切知道如何。无论如何,这自 2015 年以来没有改变。对于一些编译配置和代码来说总是可能的。我想说这种可能性仍然太脆弱,无法将类名用于应用程序逻辑。你依赖的类名可能会在源代码一次意外更改后变成垃圾
2021-03-25 16:26:38
好的,我通过查看代码发现了正在发生的事情。我的解决方案修复了类到函数的转换。所以它在缩小之前有帮助。但不是缩小问题。我必须继续挖掘,因为我无法理解我使用的所有代码如何constructor.name在缩小版本中仍然有效......不合逻辑:/
2021-03-27 16:26:38
老实说,我没有挖掘更多的文档和它帮助我使用这个配置的事实......我正在使用 ECSY 到 babel 转译项目中,它需要这个参数来获取有效的类名:github.com/MozillaReality/ecsy/issues/ 119
2021-03-28 16:26:38
你能进一步解释这应该如何帮助缩小吗?class Foo {}将被缩小到类似于class a{}任何目标的东西Foo缩小后的源代码中将没有字。
2021-04-01 16:26:38