Mir wurde heute gesagt, dass es möglich ist, eine Funktion ohne Klammern aufzurufen. Die einzige Möglichkeit, die mir einfiel, war die Verwendung von Funktionen wie apply
oder call
.
f.apply(this);
f.call(this);
Aber diese erfordern Klammern auf apply
und call
uns auf Platz eins verlassen. Ich habe auch überlegt, die Funktion an eine Art Event-Handler wie z setTimeout
:
setTimeout(f, 500);
Aber dann stellt sich die Frage: “Wie rufen Sie auf setTimeout
ohne Klammern?”
Was ist also die Lösung für dieses Rätsel? Wie können Sie eine Funktion in Javascript aufrufen, ohne Klammern zu verwenden?
Es gibt verschiedene Möglichkeiten, eine Funktion ohne Klammern aufzurufen.
Nehmen wir an, Sie haben diese Funktion definiert:
function greet() {
console.log('hello');
}
Dann folgen hier einige Anrufmöglichkeiten greet
ohne Klammern:
1. Als Konstrukteur
Mit new
Sie können eine Funktion ohne Klammern aufrufen:
new greet; // parentheses are optional in this construct.
Von MDN auf der new
Betreiber:
Syntax
new constructor[([arguments])]
2. Als toString
oder valueOf
Implementierung
toString
und valueOf
sind spezielle Methoden: Sie werden implizit aufgerufen, wenn eine Konvertierung erforderlich ist:
var obj = {
toString: function() {
return 'hello';
}
}
'' + obj; // concatenation forces cast to string and call to toString.
Sie könnten dieses Muster zum Anrufen (missbrauchen) verwenden greet
ohne Klammern:
'' + { toString: greet };
Oder mit valueOf
:
+{ valueOf: greet };
valueOf
und toString
werden in der Tat von der aufgerufen @@zuPrimitiv Methode (seit ES6), und so können Sie auch implementieren das Methode:
+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }
2.b Überschreiben valueOf
im Funktionsprototyp
Sie könnten die vorherige Idee übernehmen, um das zu überschreiben valueOf
Methode auf der Function
Prototyp:
Function.prototype.valueOf = function() {
this.call(this);
// Optional improvement: avoid `NaN` issues when used in expressions.
return 0;
};
Wenn Sie das getan haben, können Sie schreiben:
+greet;
Und obwohl es auf der ganzen Linie Klammern gibt, hat der eigentliche auslösende Aufruf keine Klammern. Mehr dazu im Blog “Methoden in JavaScript aufrufen, ohne sie wirklich aufzurufen”
3. Als Generator
Sie könnten a definieren Generatorfunktion (mit *
), die eine zurückgibt Iterator. Sie können es mit aufrufen Spread-Syntax oder mit dem for...of
Syntax.
Zuerst brauchen wir eine Generatorvariante des Originals greet
Funktion:
function* greet_gen() {
console.log('hello');
}
Und dann nennen wir es ohne Klammern, indem wir die definieren @@Iterator Methode:
[...{ [Symbol.iterator]: greet_gen }];
Normalerweise hätten Generatoren a yield
Schlüsselwort irgendwo, aber es ist nicht erforderlich, damit die Funktion aufgerufen wird.
Die letzte Anweisung ruft die Funktion auf, aber das könnte auch damit gemacht werden Destrukturierung:
[,] = { [Symbol.iterator]: greet_gen };
oder ein for ... of
konstruieren, aber es hat eigene Klammern:
for ({} of { [Symbol.iterator]: greet_gen });
Beachten Sie, dass Sie kann Machen Sie das oben mit dem Original greet
funktionieren auch, aber es wird eine Ausnahme im Prozess auslösen, nach greet
wurde ausgeführt (getestet auf FF und Chrome). Sie könnten die Ausnahme mit a verwalten try...catch
Block.
4. Als Getter
@ jehna1 hat eine vollständige Antwort darauf, also gib ihm Anerkennung. Hier ist eine Möglichkeit, eine Funktion aufzurufen Klammern-weniger auf globaler Ebene, Vermeidung der veraltet __defineGetter__
Methode. Es benutzt Object.defineProperty
stattdessen.
Wir müssen eine Variante des Originals erstellen greet
Funktion dazu:
Object.defineProperty(window, 'greet_get', { get: greet });
Und dann:
greet_get;
Ersetzen window
mit was auch immer Ihr globales Objekt ist.
Man könnte das Original nennen greet
funktionieren, ohne eine Spur auf dem globalen Objekt zu hinterlassen, wie folgt:
Object.defineProperty({}, 'greet', { get: greet }).greet;
Aber man könnte argumentieren, dass wir hier Klammern haben (obwohl sie nicht an der eigentlichen Invokation beteiligt sind).
5. Als Tag-Funktion
Seit ES6 können Sie eine Funktion aufrufen, indem Sie a übergeben Vorlagenliteral mit dieser Syntax:
greet``;
Sehen “Tagged Template-Literale”.
6. Als Proxy-Handler
Seit ES6 können Sie a definieren Proxy:
var proxy = new Proxy({}, { get: greet } );
Und dann wird das Lesen eines beliebigen Eigenschaftswerts aufgerufen greet
:
proxy._; // even if property not defined, it still triggers greet
Davon gibt es viele Variationen. Noch ein Beispiel:
var proxy = new Proxy({}, { has: greet } );
1 in proxy; // triggers greet
7. Als Instanzprüfer
Der instanceof
Operator führt die aus @@hasInstance
Methode auf dem zweiten Operanden, wenn definiert:
1 instanceof { [Symbol.hasInstance]: greet } // triggers greet
Am einfachsten geht das mit der new
Operator:
function f() {
alert('hello');
}
new f;
Das ist zwar unorthodox und unnatürlich, aber es funktioniert und ist vollkommen legal.
Der new
Der Operator benötigt keine Klammern, wenn keine Parameter verwendet werden.
Sie können Getter und Setter verwenden.
var h = {
get ello () {
alert("World");
}
}
Führen Sie dieses Skript einfach mit:
h.ello // Fires up alert "world"
Bearbeiten:
Wir können sogar argumentieren!
var h = {
set ello (what) {
alert("Hello " + what);
}
}
h.ello = "world" // Fires up alert "Hello world"
Bearbeiten 2:
Sie können auch globale Funktionen definieren, die ohne Klammern ausgeführt werden können:
window.__defineGetter__("hello", function() { alert("world"); });
hello; // Fires up alert "world"
Und mit Argumenten:
window.__defineSetter__("hello", function(what) { alert("Hello " + what); });
hello = "world"; // Fires up alert "Hello world"
Haftungsausschluss:
Wie @MonkeyZeus feststellte: Niemals sollten Sie dieses Stück Code in der Produktion verwenden, egal wie gut Ihre Absichten sind.
Hier ist ein Beispiel für eine bestimmte Situation:
window.onload = funcRef;
Obwohl diese Aussage nicht wirklich ist aufrufen aber führt zu a zukünftige Berufung.
Aber ich denke, Grauzonen könnten für Rätsel wie dieses in Ordnung sein 🙂
Wenn wir einen lateralen Denkansatz akzeptieren, gibt es in einem Browser mehrere APIs, die wir missbrauchen können, um beliebiges JavaScript auszuführen, einschließlich des Aufrufs einer Funktion, ohne tatsächliche Klammerzeichen.
1. location
und javascript:
Protokoll:
Eine solche Technik besteht darin, das zu missbrauchen javascript:
Protokoll an location
Abtretung.
Arbeitsbeispiel:
location='javascript:alertx281x29'
Obwohl technisch x28
und x29
sind immer noch Klammern, sobald der Code ausgewertet wird, die eigentliche (
und )
Zeichen erscheint nicht. Die Klammern werden in einer JavaScript-Zeichenfolge maskiert, die bei der Zuweisung ausgewertet wird.
2. onerror
und eval
:
Ebenso können wir je nach Browser die globale onerror
indem Sie es auf setzen eval
, und etwas werfen, das zu gültigem JavaScript stringifiziert wird. Dieser ist kniffliger, da Browser in diesem Verhalten inkonsistent sind, aber hier ist ein Beispiel für Chrome.
Arbeitsbeispiel für Chrome (nicht Firefox, andere ungetestet):
window.onerror=eval;Uncaught=0;throw';alertx281x29';
Dies funktioniert in Chrome, weil throw'test'
wird bestehen 'Uncaught test'
als erstes Argument zu onerror
, was fast gültiges JavaScript ist. Wenn wir es stattdessen tun throw';test'
es wird vergehen 'Uncaught ;test'
. Jetzt haben wir gültiges JavaScript! Einfach definieren Uncaught
und ersetzen Sie test durch die Nutzlast.
Abschließend:
Ein solcher Code ist wirklich schrecklichund sollte niemals verwendet werden, wird aber manchmal bei XSS-Angriffen verwendet, daher ist die Moral der Geschichte, sich nicht auf das Filtern von Klammern zu verlassen, um XSS zu verhindern. Verwendung einer CSP es wäre auch eine gute Idee, solchen Code zu verhindern.
.
Ich versuche nicht, unhöflich zu sein, aber ich muss fragen: Warum?
– jehna1
11. März 16 um 20:45 Uhr
@ jehna1 Weil ich eine Frage beantwortet habe, wie Funktionen aufgerufen werden, und korrigiert wurde, als ich sagte, dass sie am Ende Klammern benötigen.
– Mike Gluck
11. März 16 um 20:46 Uhr
Tolle! Ich hätte nicht gedacht, dass diese Frage so viel Zugkraft haben würde. Vielleicht hätte ich sie selbst stellen sollen 🙂
– Amit
12. März 16 um 6:17 Uhr
Das ist die Art von Frage, die mir das Herz bricht. Welchen Nutzen könnte ein solcher Missbrauch und eine solche Ausbeutung bringen? Ich möchte einen gültigen Anwendungsfall wissen, wo
<insert any solution>
ist objektiv besser oder nützlicher alsf()
.– Mulan
13. März 16 um 19:37 Uhr
@Aaron: Sie sagen, es gibt keinen triftigen Grund, aber wie die Antwort von Alexander O’Mara zeigt, könnte man sich die Frage stellen, ob das Entfernen von Klammern ausreicht, um die Codeausführung zu verhindern – oder was ist mit einem verschleierten Javascript-Wettbewerb? Natürlich ist es ein absurdes Ziel, wartbaren Code zu schreiben, aber es ist eine amüsante Frage.
– PJTrail
16. März 16 um 15:23 Uhr