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:
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])]
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 };
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”
È 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.
@ 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).
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à.
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.
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`;