Richiamo di una funzione senza parentesi

Oggi mi è stato detto che è ansible invocare una funzione senza parentesi. L’unico modo in cui potevo pensare era usare funzioni come apply o call .

 f.apply(this); f.call(this); 

Ma questi richiedono parentesi su call e call lasciandoci al punto di partenza. Ho anche considerato l’idea di passare la funzione a qualche tipo di gestore di eventi come setTimeout :

 setTimeout(f, 500); 

Ma poi la domanda diventa “come si fa a setTimeout senza parentesi?”

Quindi qual è la soluzione a questo enigma? Come puoi invocare una funzione in Javascript senza usare parentesi?

Esistono diversi modi per chiamare una funzione senza parentesi.

Supponiamo che tu abbia questa funzione definita:

 function greet() { console.log('hello'); } 

Quindi ecco alcuni modi per chiamare il greet senza parentesi:

1. Come costruttore

Con new puoi invocare una funzione senza parentesi:

 new greet; // parentheses are optional in this construct. 

Da MDN sul new oprator :

Sintassi

 new constructor[([arguments])] 

2. Come per valueOf o valueOf implementazione

toString e valueOf sono metodi speciali: vengono chiamati implicitamente quando è necessaria una conversione:

 var obj = { toString: function() { return 'hello'; } } '' + obj; // concatenation forces cast to string and call to toString. 

Potresti (ab) usare questo modello per chiamare il greet senza parentesi:

 '' + { toString: greet }; 

O con valueOf :

 +{ valueOf: greet }; 

2.b valueOf override in Function Prototype

Si potrebbe prendere l’idea precedente di sovrascrivere il metodo valueOf sul prototipo Function :

 Function.prototype.valueOf = function() { this.call(this); // Optional improvement: avoid `NaN` issues when used in expressions. return 0; }; 

Dopo averlo fatto, puoi scrivere:

 +greet; 

E anche se ci sono parentesi coinvolte lungo la linea, l’effettiva chiamata di innesco non ha parentesi. Vedi di più su questo nel blog “Chiamare i metodi in JavaScript, senza chiamarli veramente”

3. Come generatore

È ansible definire una funzione generatore (con * ), che restituisce un iteratore . Puoi chiamarlo usando la syntax di diffusione o con il for...of syntax.

Per prima cosa abbiamo bisogno di una variante del generatore della funzione di greet originale:

 function* greet_gen() { console.log('hello'); } 

E poi lo chiamiamo senza parentesi:

 [...{ [Symbol.iterator]: greet_gen }]; 

Normalmente i generatori avrebbero una parola chiave yield , da qualche parte, ma non è necessaria per ottenere la chiamata alla funzione.

L’ultima istruzione invoca la funzione, ma potrebbe anche essere eseguita con la destrutturazione :

 [,] = { [Symbol.iterator]: greet_gen }; 

o un for ... of costrutto:

 for ({} of { [Symbol.iterator]: greet_gen }); 

Nota che puoi fare anche questo con la funzione di greet originale, ma attiverà un’eccezione nel processo, dopo che il greet è stato eseguito (testato su FF e Chrome). È ansible gestire l’eccezione con un try...catch block.

4. Come Getter

@ jehna1 ha una risposta completa su questo, quindi dagli il merito. Ecco un modo per chiamare una funzione parentesi-less sull’ambito globale, evitando il deprecato metodo __defineGetter__ . Utilizza invece Object.defineProperty .

Abbiamo bisogno di creare una variante della funzione di greet originale per questo:

 Object.defineProperty(window, 'greet_get', { get: greet }); 

E poi:

 greet_get; 

Sostituisci la window con qualunque sia il tuo object globale.

È ansible chiamare la funzione di greet originale senza lasciare una traccia sull’object globale come questo:

 Object.defineProperty({}, 'greet', { get: greet }).greet; 

Ma si potrebbe sostenere che qui abbiamo parentesi (anche se non sono coinvolte nell’effettiva invocazione).

5. Come funzione tag

Con ES6 puoi chiamare una funzione passandogli un modello letterale con questa syntax:

 greet``; 

Vedi “Tagged Template Literals” . Normalmente non si passerebbe un letterale vuoto come qui, ma come esempio di una chiamata tra parentesi , lo farà.

6. Come gestore proxy

In ES6, puoi definire un proxy :

 var proxy = new Proxy({}, { get: greet } ); 

E poi la lettura di qualsiasi valore di proprietà invocherà il greet :

 proxy._; // even if property not defined, it still triggers greet 

Ci sono molte varianti di questo. Un altro esempio:

 var proxy = new Proxy({}, { has: greet } ); 1 in proxy; // triggers greet 

Il modo più semplice per farlo è con il new operatore:

 function f() { alert('hello'); } new f; 

Puoi usare getter e setter.

 var h = { get ello () { alert("World"); } } 

Esegui questo script solo con:

 h.ello // Fires up alert "world" 

Modificare:

Possiamo persino fare argomenti!

 var h = { set ello (what) { alert("Hello " + what); } } h.ello = "world" // Fires up alert "Hello world" 

Modifica 2:

È inoltre ansible definire funzioni globali che possono essere eseguite senza parentesi:

 window.__defineGetter__("hello", function() { alert("world"); }); hello; // Fires up alert "world" 

E con argomenti:

 window.__defineSetter__("hello", function(what) { alert("Hello " + what); }); hello = "world"; // Fires up alert "Hello world" 

Disclaimer:

Come @MonkeyZeus ha dichiarato: Non utilizzerai mai questo pezzo di codice in produzione, indipendentemente dalle tue intenzioni.

Ecco un esempio per una situazione particolare:

 window.onload = funcRef; 

Anche se questa affermazione non sta effettivamente invocando, ma porterà a una futura invocazione .

Ma, immagino che le aree grigie potrebbero andare bene per indovinelli come questo 🙂

Se accettiamo un approccio di pensiero laterale, in un browser ci sono diverse API che possiamo abusare per eseguire JavaScript arbitrario, inclusa la chiamata di una funzione, senza alcuna parentesi effettiva.

1. location e javascript: protocollo:

Una di queste tecniche consiste nell’abusare il javascript: protocollo sull’assegnazione della location .

Esempio di lavoro:

 location='javascript:alert\x281\x29' 

In ES6, hai ciò che si chiama Tagged Template Literals .

Per esempio:

 function foo(val) { console.log(val); } foo`Tagged Template Literals`;