If I want to dynamically create new classes (not instances) on the fly in EcmaScript, how can I do this? And how can those instances extend
from another class?
Traditionally one could make a "class":
function Living(){}
Living.prototype.eat = function(){ console.log("eat") }
Then dynamically extend it:
function makeSubclass(name, parentClass){
var klass = new Function()
klass.name = name
klass.displayName = name
klass.prototype = parentClass.prototype
klass.prototype.constructor = klass
}
var Animal = makeSubclass("Animal", Living)
var dog = new Animal()
dog.eat() //=> eat
And so on:
Animal.prototype.walk = function(){ console.log("one foot then another") }
var Person = makeSubclass("Person", Animal)
var rektide = new Person()
rektide.eat() //=> eat
rektide.walk() //=> one foot then another
The key is that I wish to do so dynamically, in a data-driven manner. This traditional approach let's me dynamically create new classes:
fetch("http://yoyodyne.net/animals").then(response => response.json()).then(animals => {
animals.forEach(animalName => window[animalName] = makeSubclass(animalName, Animal)
})
And thus I can dynamically, data-drivenly create the classes I need.
The theory I thought I was told was that EcmaScript classes were just syntax sugar, and that I could keep using old syntax. However we are starting to see examples where only EcmaScript classes will do (such as Custom Elements) and so this ability to on the fly define new classes (as opposed to using a class expression, which is a static construction) becomes important.
In the Custom Elements needing new class
syntax issue, Domenic Denicola writes
I hope you're aware that you can generate classes dynamically just as easily as functions,
And so I'd like to ask, how? If I want a new class that extends HTMLElement, how can I create that on the fly, in a data-driven manner?
Use a class expression:
class Living {
eat() {
return 42
}
}
const livingThings = {}
;['dog', 'cat'].forEach((name) => (
livingThings[name] = class extends Living {
name() { return name }
}
))
const myDog = new livingThings.dog()
myDog.eat() //=> 42
myDog.name() //=> 'dog'
The superclass here (Living
), can be replaced with anything, as it’s an expression (Living
, after all, is just like any other variable).
That said, I would try to avoid dynamically creating classes, if possible, and instead use instances.