Forum: PC-Programmierung [ES6] Wie Klassenobjekt Proxifizieren vor Kontruktoraufruf


von DPA (Gast)


Lesenswert?

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?

von DPA (Gast)


Lesenswert?

Ich habe zwar keine echte Lösung für das Problem gefunden, aber ein 
Workaround:
1
class A {
2
  init(){}
3
};
4
5
function f(){
6
  this.init();
7
}
8
f.prototype = A.prototype;
9
let o = new f();

Apply geht nur beim constructor nicht, bei einer anderen Methode, hier 
init, aber schon. Mit der Funktion f als abgeleitete Klasse muss ich den 
Konstruktor nicht aufrufen, und kann diesen damit "unschädlich" machen. 
Dann initialisiere ich das einfach in einer init Methode. Und vor deren 
Aufruf kann ich dort den this parameter bequem in einen Proxy verpacken. 
Nicht perfekt, aber ein brauchbarere Workaround. Als bonus kann ich init 
sogar async machen, noch was, was mit einem constructor nicht ginge!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.