用javascript动态扩展类



我们如何动态/编程地扩展javascript类?

更具体地说,给定这样的东西

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;
});

我们如何实现像extend这样的东西,使它的行为与相同

class Rectangle extends Polygon {
constructor(length, width) {
super(length * width, 4);
this.length = length;
this.width = width;
}
}

这里有三个问题:

(1( super仅在对象方法内部可用,因此无法在箭头函数中访问super。这需要以某种方式被一个常规函数调用所取代。

(2( 类只能被构造,不能被调用(与充当构造函数的函数不同(。因此,不能只将类构造函数.call放到"子类"实例上。您必须创建一个超类的实例并将其复制到子类中,最终会丢失getters/ssetter。

(3( Arrow函数有一个词法this,因此不能在Arrow函数内访问具有this的实例。

考虑到这三个问题,一个可行的替代方案是:

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(/*...*/);
/*...*/
});

但老实说。。。原生class ... extends出了什么问题?

经过一些黑客攻击,我发现这非常有效。

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;
});

这对我有效:

// 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

这将动态扩展任何给定的父类

https://jsfiddle.net/u15482b6/2/

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()

我需要扩展一个google maps js lib,它是异步加载的,并且只通过回调通知准备好。所以

<script src="google?callback=xxx">
<script src="me">

当我的代码定义class extends google.maps.xxx时,google.maps可能不存在。

所以我用了一个初始化函数

init = () => { class child extends google.xxx { ... } }

如果直到运行时才知道扩展类的名称,则可以使用eval。如果知道,请小心的安全隐患

最新更新