Ich habe die (vielleicht etwas verrückte) Idee nun schon seit ein paar
Jahren, aber ich kann einfach keine Lösung dafür finden.
Es geht darum, mittels eines Proxy Objekts sämtliche Zugriffe eines
Objekts, inklusive die durch den Konstruktor, welcher dieses
initialisiert, abzufangen.
Mit altmodischen Funktionen war das relativ einfach, dort konnte man
einfach call oder apply benutzen, aber bei ES6 Klassen geht das nicht
mehr:
1 | function A(){
|
2 | this.test = 123;
|
3 | }
|
4 |
|
5 | class B {
|
6 | constructor(){
|
7 | this.test = 123;
|
8 | }
|
9 | }
|
10 |
|
11 | // statt "new T(...args)", manuell das selbe tun, aber Proxy darum legen
|
12 | function proxify(T, ...args){
|
13 | let proxy = new Proxy(Object.create(T.prototype), {
|
14 | set(that, prop, value){
|
15 | console.log(prop, value);
|
16 | that.prop = value;
|
17 | }
|
18 | });
|
19 | T.call(proxy, ...args);
|
20 | return proxy;
|
21 | }
|
22 |
|
23 | let a = proxify(A); // Ausgabe: test 123
|
24 | let b = proxify(B); // Fehler: Uncaught TypeError: Class constructor B cannot be invoked without 'new'
|
In ES6 wurde apply mit Konstruktoren verboten, damit man nicht
versehentlich mit A() globale Variablen erstellt usw.
Meine nächste Idee war, die constructor prototype chain zu hijacken:
1 | function proxify($class, interceptor, ...args){
|
2 | if(!$class || !($class instanceof Function))
|
3 | throw new TypeError("$class must be a class or function");
|
4 | let topproto = $class, next=Object.getPrototypeOf(topproto);
|
5 | if(!next || !(next instanceof Function))
|
6 | throw new TypeError("Unsupported prototype chain");
|
7 | while(next instanceof Function){
|
8 | topproto = next;
|
9 | next = Object.getPrototypeOf(next);
|
10 | }
|
11 | console.log(topproto, next);
|
12 | Object.setPrototypeOf(topproto, interceptor);
|
13 | let error = null;
|
14 | try {
|
15 | var result = new $class(...args);
|
16 | } catch(e) { error = e; }
|
17 | Object.setPrototypeOf(topproto, next);
|
18 | if(error)
|
19 | throw error;
|
20 | return result;
|
21 | }
|
22 |
|
23 | let x = proxify(B, function(){
|
24 | return new Proxy(this, {
|
25 | set(that, prop, value){
|
26 | console.log(prop, value);
|
27 | that.prop = value;
|
28 | }
|
29 | });
|
30 | });
|
Funktioniert aber leider nicht. Bei A extends B extends C könnte ich vor
C und vor B eingreifen und das Objekt in einen Proxy einbetten, aber
nicht nach C, weil die oberste klasse anscheinend den super Construktor
nicht aufruft...
Dann hab ich es über den Prototype versucht:
1 | B.prototype = new Proxy(this, {
|
2 | set(that, prop, value){
|
3 | console.log(prop, value);
|
4 | that.prop = value;
|
5 | }
|
6 | });
|
7 | (new B()).test = 123;
|
War leider auch nichts.
Hat sonst noch jemand eine Idee, wie man das eventuell doch noch
hinkriegen könnte?