[ES6] Wie Klassenobjekt Proxifizieren vor Kontruktoraufruf

Gast #6152602
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?
Gast
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!

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 6 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.