I have been experimenting with ES6 classes and am wondering if you can change class names dynamically? For example
class [Some dynamic name] {};
I have been experimenting with ES6 classes and am wondering if you can change class names dynamically? For example
class [Some dynamic name] {};
There is probably a better solution for whatever you are trying to achieve, but you can assign a class expression to an object:
let classes = {};
classes[someName] = class { ... };
This didn't really change in ES2015: if you want to create a dynamically named binding, you have to use an object or some other mapping instead.
.name
is now someName
. –
Rigidify {[someName]: class {…}}
–
Rigidify class
value of course). –
Minetta var Foo = class {}
. In Chrome, the class doesn't get named. :( –
Belgrade class Foo {}
anyway. –
Rigidify let Foo; Foo = class {};
? Should that work too? Or does it have to be during the declaration? –
Belgrade class base {}; var classes = {}; classes.test = class extends base{}; classes.test.name
should return 'test' but it returns 'base' –
Barnstorm name
of the function is not of concern in this answer, only how to refer to it in code. I never made the claim that the name
property has a specific value. –
Minetta let C = class
{ // ...
}
Object.defineProperty (C, 'name', {value: 'TheName'});
// test:
let itsName = (new C()).constructor.name;
// itsName === 'TheName' -> true
Object.defineProperty(C, 'name', ...)
works while C.name = ...
throws Script Error: "name" is read-only
(at least in Firefox). –
Crawley Object.defineProperty()
supposed to allow modification of read-only properties, or is that just an implementation quirk that's not supposed to be there? –
Boodle console.log(new C())
it prints C {}
, but TheName {}
would be better. –
Macbeth There is a pretty simple way to do it:
const nameIt = (name, cls) => ({[name] : class extends cls {}})[name];
Here's the demo.
It uses an object literal to define a field with a desired name that would hold a new class. This causes the new class to automatically get the desired name. After we're done with that, we extract that new class and return it.
Note the parens around the object literal, so that curly braces don't get mistaken for a code block (...) => {...}
.
Of course, putting an existing class into named fields won't change the class, so this only works if you are creating a new class. If you only need a dynamic name in one place where you define the class you are naming, you can drop an extra inheritance and just go:
const myClass = {[name]: class {
...
}}[name];
static create = (args) => new this(args);
In such case the name will be "_class" –
Overpower There is probably a better solution for whatever you are trying to achieve, but you can assign a class expression to an object:
let classes = {};
classes[someName] = class { ... };
This didn't really change in ES2015: if you want to create a dynamically named binding, you have to use an object or some other mapping instead.
.name
is now someName
. –
Rigidify {[someName]: class {…}}
–
Rigidify class
value of course). –
Minetta var Foo = class {}
. In Chrome, the class doesn't get named. :( –
Belgrade class Foo {}
anyway. –
Rigidify let Foo; Foo = class {};
? Should that work too? Or does it have to be during the declaration? –
Belgrade class base {}; var classes = {}; classes.test = class extends base{}; classes.test.name
should return 'test' but it returns 'base' –
Barnstorm name
of the function is not of concern in this answer, only how to refer to it in code. I never made the claim that the name
property has a specific value. –
Minetta To take it a bit further playing with dynamic class names and dynamic inheritance, when using babel you can just do something like this:
function withname(name, _parent) {
return class MyDinamicallyNamedClass extends (_parent||Object) {
static get name() { return name || _parent.name }
}
}
class MyDinamicallyNamedClass ...
–
Belgrade MyDinamicallyNamedClass instanceof _parent
) returns true \n (btw thats not how you spell Dynamically, lOlz) –
Transported One way, even if not ideal, is simple with eval
:
~function() {
const name = "Lorem"
eval(`
var ${name} = class ${name} {}
`)
console.log(Lorem) // class Lorem {}
}()
Note, it has to be with var
. Using let
, const
, and plain class
inside the eval
won't work.
Another way with Function
:
~function() {
const name = "Lorem"
const c = new Function(`
return class ${name} {}
`)()
console.log(c) // class Lorem {}
}()
Sitenote: you can pass scope variables into the Function
and use them inside:
~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"
}()
Function
this way is also eval
and carries the same risks. –
Pharisee Function
or eval
. It isn't a problem if you own the code your sticking in there (f.e. the code string is generated in the same scope as the Function
or eval
, with no input from the outside). –
Belgrade Function
or eval
. It isn't a problem if you own the code your sticking in there (f.e. the code string is generated in the same scope as the Function
or eval
, with no input from the outside). As you can see in my example, the strings are generated in-place, which is completely safe (and assuming my code is inside a module, it is impossible for variables to be modified from the outside). –
Belgrade eval
. I strongly encourage you to add a warning to that effect. –
Pharisee I can suggest another type of approach, just async but it works:
let protoExtend = function ( className, extendsFrom = Array ) {
return new Promise( function ( done, fail ) {
const src = URL.createObjectURL(
new Blob( [`
self.${className}(
class ${className} extends ${extendsFrom.name} {}
);`
] )
);
self[ className ] = function ( prototype ){
URL.revokeObjectURL(src);
delete self[ className ];
return done( prototype );
};
document.head.append( Object.assign(
document.createElement("script"), { src }
));
});
};
triggering class constructor with parent class and a name for new child class:
let myClass = await protoExtend("HelloWorld", String);
now we have what we wanted: dynamically created children without any eval:
new myClass('test');
result can be shown at console as well: console result image
© 2022 - 2024 — McMap. All rights reserved.
Function
constructor and use the prototype as you would have in ES5. Or use a task running like Gulp/Grunt to generate the classes pre-build – Octavalentconstructor.name
is not immutable. – Rigidifyclass
es just as well) – Rigidifylet tmp = { [name](){} };
and to access that named functiono[name]
. Would be cool if we could do that with classes:let o = { class [name] {} }
. – BelgradeObject.defineProperty(constructor, "name", {value:…})
. Also, you can add astatic name() { … }
method or getter to the class itself. – Rigidifyclass Foo extends multiple(One, Two, Three) {...}
that combines the classes together using Proxy. The real reason I want dynamic class naming is because I can then take a user class spec and generate a class based on a given name, and provide API likeClass('Foo').extends(One, Two, { ... }) which automatically generates a class that duals as class and class-factory mixin. We could also transpile normal
class Foo extends (One, Two, Three) {}` – Belgrade