ES6 动态类名

69

我一直在尝试使用ES6类,想知道是否可以动态更改类名?比如

class [Some dynamic name] {}; 

1
为什么你想要一个动态名称的类?有什么意义呢? - CodingIntrigue
1
这就是全部了。实例应该是动态的,而不是你的类模板,但如果你确信这是正确的方法,你可以使用Function构造函数创建一个标准函数(使用JavaScript中的动态函数名),并像在ES5中一样使用原型。或者使用类似Gulp/Grunt的任务运行器在预构建时生成类。 - CodingIntrigue
1
@CodingIntrigue,我来这里是为了寻找一些东西(即动态类创建),但是你的评论“使用像Gulp/Grunt这样的任务运行器来生成类”让我朝着不同的方向前进,解决了我没有想到的很多问题。谢谢...... - Ananda
9
这句话的意思是:这就是全部,实例应该是动态的,而不是你的类模板。 我非常不同意。作为来自高度静态的Java语言的人,我越来越欣赏JS的动态性。我想要动态地设置类名,这样我的类就不会是匿名的。我还想动态创建类,因为我正在开发一个库,用于创建和使用混入以实现ES6类的多重继承。我使用传统继承的时间越长,就越确信单一继承无法胜任。JS之所以非常棒,正是因为它是动态的,而不是尽管如此。 - Stijn de Witt
1
@StijndeWitt 说得好。我来到这里是因为我也在尝试做同样的事情。我一直在尝试使用类工厂mixin和类似于class Foo extends multiple(One, Two, Three){...}这样的工具,该工具使用Proxy将多个类组合在一起。我想要动态定义类名的真正原因是因为我可以根据给定名称生成基于用户类规范的类,并提供API,如 Class('Foo').extends(One, Two, { ... }),它会自动生成一个双重作用的类和类工厂mixin。我们还可以转换普通的class Foo extends (One, Two, Three) {} - trusktr
显示剩余14条评论
5个回答

68
let C = class
{ // ...
}
Object.defineProperty (C, 'name', {value: 'TheName'});

// test: 
let itsName =  (new C()).constructor.name;
// itsName === 'TheName' -> true

6
你好,如果你能提供一份代码解释将会非常有帮助。 - tima
我相信这是正确的答案。 注意:在 Firefox 中,Object.defineProperty(C,'name',...) 可以工作,而 C.name = ... 会抛出“脚本错误:“name”是只读的”。 - Wiktor Tomczak
这应该是正确的答案,被接受的答案类没有名称。 - Marcos Casagrande
1
是的,截至当前日期,这是正确的答案。没有办法在类定义本身中完成这个。 - Hoffmann
Object.defineProperty() 是否允许修改只读属性,还是这只是一个不应该存在的实现问题? - David Given
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty - Panu Logic

30

有一种相当简单的方法:

const nameIt = (name, cls) => ({[name] : class extends cls {}})[name];

这里是演示。

它使用对象字面量来定义一个字段,该字段具有所需的名称并保存一个新类。这将使得新类自动获得所需的名称。完成后,我们提取该新类并返回它。

请注意对象字面量周围的圆括号,以便花括号不会被误认为是代码块(...) => {...}

当然,将现有类放入命名字段中不会更改类,因此仅在创建新类时才起作用。如果您只需要在定义要命名的类的位置上动态地使用名称,可以省略额外的继承,只需执行以下操作:

const myClass = {[name]: class {
    ...
}}[name];

我们可以再添加一个方法吗?并且再次添加新的属性吗? - Zum Dummi
如果我们有一个静态方法将类作为“this”引用,那么这种技术将无法使用。 例如: static create = (args) => new this(args);在这种情况下,名称将是“_class”。 - pPanda_beta

24

也许有更好的解决方案能够实现你的目标,但你可以为对象分配一个类表达式:

let classes = {};
classes[someName] = class { ... };

在ES2015中,这并没有真正改变:如果你想创建一个动态命名的绑定,你必须使用一个对象或其他映射而不是直接使用变量名。


实际上,ES6 的变化是类的 .name 现在是 someName - Bergi
1
或者等等,那只是在执行 {[someName]: class {…}} 时。 - Bergi
1
如何向这样的类表达式添加装饰器? - Jaanus Varus
@Bergi 但这是否是规范化的?还是只是引擎可能会做的事情(根据变量分配或属性分配命名类)? - trusktr
@trusktr 那就是Chrome的一个bug,根据规范应该会有一个名称。但是你最好还是写class Foo {} - Bergi
显示剩余10条评论

4

一种方式,即使不是理想的方式,也可以使用eval来简化:

~function() {
    const name = "Lorem"

    eval(`
        var ${name} = class ${name} {} 
    `)

    console.log(Lorem) // class Lorem {}
}()

请注意,必须使用var。在eval中使用letconst和普通的class是行不通的。
另一种方法是使用Function
~function() {
    const name = "Lorem"

    const c = new Function(`
        return class ${name} {}
    `)()

    console.log(c) // class Lorem {}
}()

注:您可以将作用域变量传递到Function中并在内部使用它们。
~function() {
    const name = "Lorem"
    const val = "foo"

    const Class = new Function('val', `
        return class ${name} {
            constructor() {
                console.log( val )
            }
        }
    `)( val )

    console.log(Class) // class Lorem {}
    new Class // "foo"
}()

1
请注意,这种使用“Function”的方式也是“eval”,并具有相同的风险。 - eyelidlessness
3
只有在将第三方代码放入Functioneval中时才会成为问题。如果你所拼接的代码是你自己编写的(例如,该代码字符串是在与Functioneval相同的作用域中生成的,并且没有来自外部的输入),那么这不是一个问题。 - trusktr
@eyelidlessness 只有在将第三方代码放入“Function”或“eval”中时才会出现问题。如果您拥有要插入的代码(例如,代码字符串在与“Function”或“eval”相同的范围内生成,没有来自外部的输入),则不会出现问题。正如您可以在我的示例中看到的那样,字符串是在原地生成的,这是完全安全的(并且假设我的代码在模块内部,从外部修改变量是不可能的)。 - trusktr
我已经更新了我的示例,使用闭包(类似于模块)。 - trusktr
2
这仍然值得指出,因为人们倾向于采用代码示例并对其进行扩展。对于不太熟悉此操作方式的人来说,它使用了“eval”可能不是立即显而易见的。我强烈建议您添加相应的警告。 - eyelidlessness

4
为了更深入地玩耍动态类名和动态继承,使用babel时,你可以像这样做:
    function withname(name, _parent) {
        return class MyDinamicallyNamedClass extends (_parent||Object) {
            static get name() { return name || _parent.name }
        }
    }

4
但控制台仍会输出 class MyDinamicallyNamedClass ... - trusktr
是的,但我认为他的观点是 MyDinamicallyNamedClass instanceof _parent 返回 true。顺便说一下,那不是 Dynamically 的拼写方式,哈哈。 - JΛYDΞV

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