Signatur
Beschreibung
Die Falle handler.apply() wird ausgelöst, sobald ein Proxy um eine Funktion herum aufgerufen wird. Dazu gehören der normale Aufruf proxy(...args), Function.prototype.call(), Function.prototype.apply() und Reflect.apply(). So lassen sich Funktionsaufrufe abfangen, protokollieren, prüfen oder verändern, ohne die ursprüngliche Funktion anzufassen.
Typische Einsatzgebiete sind Logging und Tracing, Prüfung der Argumente, Memoisierung (Zwischenspeichern von Ergebnissen), Zeitmessung und das Nachrüsten von Zugriffskontrollen. Innerhalb der Falle leitet man den Aufruf meist mit Reflect.apply(target, thisArg, argumentsList) an die Originalfunktion weiter.
Wichtig: Die Falle funktioniert nur, wenn das target selbst aufrufbar ist, also eine Funktion. Ein Proxy um ein normales Objekt wird durch eine apply-Falle nicht aufrufbar. Aufrufe mit new laufen nicht über apply, sondern über die Falle construct.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | Function | Die Zielfunktion, um die der Proxy liegt. | |
| $thisArg Pflicht | any | Der this-Wert des Aufrufs. Bei einem einfachen Aufruf proxy() ist er undefined, bei obj.proxy() ist es obj. |
|
| $argumentsList Pflicht | Array | Ein echtes Array mit den übergebenen Argumenten. |
Rückgabewert
Beispiele
Funktionsaufrufe protokollieren
const add = (a, b) => a + b;
const loggedAdd = new Proxy(add, {
apply(target, thisArg, args) {
console.log(`Aufruf ${target.name}(${args.join(', ')})`);
const result = Reflect.apply(target, thisArg, args);
console.log(`Ergebnis: ${result}`);
return result;
}
});
loggedAdd(2, 3);
loggedAdd.call(null, 10, 5);
Argumente prüfen
const setVolume = (level) => `Lautstärke: ${level}`;
const safeSetVolume = new Proxy(setVolume, {
apply(target, thisArg, [level]) {
if (typeof level !== 'number' || level < 0 || level > 100) {
throw new RangeError(`Ungültiger Wert: ${level}`);
}
return Reflect.apply(target, thisArg, [level]);
}
});
console.log(safeSetVolume(42));
try {
safeSetVolume(150);
} catch (err) {
console.log(err.message);
}
Memoisierung einer teuren Berechnung
const memoize = (fn) => {
const cache = new Map();
return new Proxy(fn, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const value = Reflect.apply(target, thisArg, args);
cache.set(key, value);
return value;
}
});
};
const slowSquare = (n) => {
console.log(`berechne ${n}²`);
return n * n;
};
const fastSquare = memoize(slowSquare);
console.log(fastSquare(9));
console.log(fastSquare(9));
this-Wert bei Methodenaufrufen
const user = {
name: 'Ada',
greet: new Proxy(function (greeting) {
return `${greeting}, ${this.name}!`;
}, {
apply(target, thisArg, args) {
console.log('this ist user:', thisArg === user);
return Reflect.apply(target, thisArg, args);
}
})
};
console.log(user.greet('Hallo'));
// Wichtig · Fallstricke
- Ziel muss eine Funktion sein: Ist
targetnicht aufrufbar, wirftproxy()einenTypeError, egal ob eineapply-Falle vorhanden ist. newläuft nicht überapply: Fürnew proxy()ist die Falleconstructzuständig.- Leite mit
Reflect.apply()weiter statt mittarget.apply(...). So klappt es auch, wenn die Funktion eine eigeneapply-Eigenschaft hat, die die geerbte überdeckt. - Für
applygibt es keine Invarianten. Der Rückgabewert kann also frei gewählt werden. - Leistung: Jeder Aufruf läuft durch die Falle. In sehr heißen Schleifen kann das merklich bremsen.
typeof proxyliefert'function', undproxy.namebzw.proxy.lengthwerden (ohneget-Falle) an das Ziel weitergereicht.- Kompatibilität: Wird von allen modernen Browsern und von Node.js 6+ unterstützt. Lässt sich nicht per Polyfill nachbilden, im Internet Explorer gibt es das also nicht.