How can we dynamically/programmatically extend a javascript class?
More concretely, given something like
class Polygon {
constructor(area, sides) {
this.area = area;
this.sides = sides;
}
}
const Rectangle = extend(Polygon, (length, width) => {
super(length * width, 4);
this.length = length;
this.width = width;
});
how can we implement something like extend
such that it behaves the same as
class Rectangle extends Polygon {
constructor(length, width) {
super(length * width, 4);
this.length = length;
this.width = width;
}
}
?
How can we dynamically/programmatically extend a javascript class?
More concretely, given something like
class Polygon {
constructor(area, sides) {
this.area = area;
this.sides = sides;
}
}
const Rectangle = extend(Polygon, (length, width) => {
super(length * width, 4);
this.length = length;
this.width = width;
});
how can we implement something like extend
such that it behaves the same as
class Rectangle extends Polygon {
constructor(length, width) {
super(length * width, 4);
this.length = length;
this.width = width;
}
}
?
Share Improve this question asked Apr 12, 2020 at 22:12 kag0kag0 6,0648 gold badges41 silver badges70 bronze badges 1- What you are looking for is called prototype chaining: medium./developers-arena/… – Paddy Commented Apr 12, 2020 at 22:15
5 Answers
Reset to default 3This worked for me:
// Normal class to be extended
class A {
constructor(msg, name = 'A') {
console.log('A: ' + msg + ' from ' + name)
document.getElementById("demo").innerHTML += 'A: ' + msg + ' from ' + name + '<br>'
}
}
// New class B which extends A
const B = (msg, name) => class Child extends A {
constructor(/*func params is used instead*/) {
super(msg, name || 'B')
}
}
// New class C which extends B which extends A
const C = (msg, name) => class Child extends B(msg, name) {
constructor(/*func params is used instead*/) {
super(msg)
}
}
// New class D which extends C which extends B which extends A
const D = (...args) => class Child extends C(args) {
constructor(/*func params is used instead*/) {
super(args)
}
}
new A('Hello') // A: Hello from A
new (B('Hi')) // A: Hi from B
new (C('Hey from C and')) // A: Hey from C and from B
new (D('Hey from D and C and')) // A: Hey from D and C and from B
This will dynamically extend any given parent class
https://jsfiddle/u15482b6/2/
There are three problems here:
(1) super is only available inside object methods, so there is no way to access super in an arrow function. That needs to be somehow replaced by a regular function call.
(2) Classes can only be constructed, not called (unlike functions acting as constructors). Therefore you cannot just .call
the classes constructor onto the "subclass" instance. You have to create an instance of the superclass and copy that into the subclass, eventually loosing getters / setters.
(3) Arrow functions have a lexical this
, so you cannot access the instance with this
inside an arrow function.
Given these three problems, a viable alternative would be:
function extend(superclass, constructor) {
function Extended(...args) {
const _super = (...args) => Object.assign(this, new superclass(...args));
constructor.call(this, _super, ...args);
}
Object.setPrototypeOf(Extended, superclass);
Object.setPrototypeOf(Extended.prototype, superclass.prototype);
return Extended;
}
const Rectangle = extend(Polygon, function(_super, length, width) {
_super(/*...*/);
/*...*/
});
But honestly ... what's wrong with the native class ... extends
?
After some hacking around, I've found that this horrifyingly works.
function extend(superclass, construct) {
return class extends superclass {
constructor(...args) {
let _super = (...args2) => {
super(...args2)
return this;
};
construct(_super, ...args);
}
};
}
const Rectangle = extend(Polygon, function(_super, length, width) {
let _this = _super(length * width, 4);
_this.length = length;
_this.width = width;
});
class A {
m () {
console.log('A')
}
}
class B extends A {
m () {
console.log('B')
}
}
var a = new A()
var b = new B()
a.m()
b.m()
const MixinClass = superclass =>
class extends superclass {
m () {
console.log('extended')
}
}
const extendsAnyClass = AnyClass =>
class MyMixinClass extends MixinClass(AnyClass) {}
var AA = extendsAnyClass(A)
var BB = extendsAnyClass(B)
var aa = new AA()
var bb = new BB()
aa.m()
bb.m()
I needed to extend a google maps js lib, which is loaded async, and only notified ready by a callback. So
<script src="google?callback=xxx">
<script src="me">
wont work as google.maps
may not exist when my code defines class extends google.maps.xxx
.
So I used an init function
init = () => { class child extends google.xxx { ... } }
If you don't know the name of your extended class until runtime, you can use eval
If you do, be careful about the security implications
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745370367a4624769.html
评论列表(0条)