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 greetsenza parentesi:
1. Come costruttore
Con newte puoi invocare una funzione senza parentesi:
new greet; // parentheses are optional in this construct.
Da MDN newsull'opratore :
Sintassi
new constructor[([arguments])]
2. Come toStringo valueOfimplementazione
toStringe valueOfsono 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.
È possibile (ab) utilizzare questo modello per chiamare greetsenza parentesi:
'' + { toString: greet };
O con valueOf:
+{ valueOf: greet };
valueOfe toStringsono infatti chiamati dal metodo @@ toPrimitive (dal momento che ES6), e quindi è anche possibile implementare quel metodo:
+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }
2.b Sostituzione valueOfdel prototipo della funzione
Potresti prendere l'idea precedente per sovrascrivere il valueOfmetodo sul Functionprototipo :
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 sebbene ci siano parentesi coinvolte lungo la linea, l'invocazione effettiva non ha parentesi. Scopri di più su questo nel blog "Metodi di chiamata in JavaScript, senza davvero chiamarli"
3. Come generatore
È possibile definire una funzione del generatore (con *), che restituisce un iteratore . Puoi chiamarlo usando la sintassi diffusa o con la for...ofsintassi.
Per prima cosa abbiamo bisogno di una variante del generatore della greetfunzione originale :
function* greet_gen() {
console.log('hello');
}
E quindi lo chiamiamo senza parentesi definendo il metodo @@ iterator :
[...{ [Symbol.iterator]: greet_gen }];
Normalmente i generatori avrebbero una yieldparola chiave da qualche parte, ma non è necessario che la funzione venga chiamata.
L'ultima istruzione richiama la funzione, ma ciò potrebbe essere fatto anche con la destrutturazione :
[,] = { [Symbol.iterator]: greet_gen };
o un for ... ofcostrutto, ma ha le sue parentesi:
for ({} of { [Symbol.iterator]: greet_gen });
Nota che puoi fare quanto sopra anche con la greetfunzione originale , ma innescherà un'eccezione nel processo, dopo che greet è stato eseguito (testato su FF e Chrome). È possibile gestire l'eccezione con un try...catchblocco.
4. Come Getter
@ jehna1 ha una risposta completa su questo, quindi dagli credito. Ecco un modo per chiamare una funzione tra parentesi sull'ambito globale, evitando il metodo deprecato__defineGetter__ . Usa Object.definePropertyinvece.
Per questo dobbiamo creare una variante della greetfunzione originale :
Object.defineProperty(window, 'greet_get', { get: greet });
E poi:
greet_get;
Sostituisci windowcon qualunque sia il tuo oggetto globale.
È possibile chiamare la greetfunzione originale senza lasciare una traccia sull'oggetto globale in questo modo:
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
Da ES6 puoi chiamare una funzione passandole letteralmente come template con questa sintassi:
greet``;
Vedere "Letterali modello con tag" .
6. Come gestore proxy
Da ES6, è possibile definire un proxy :
var proxy = new Proxy({}, { get: greet } );
E quindi la lettura di qualsiasi valore di proprietà invocherà 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
7. Come controllo istanza
L' instanceofoperatore esegue il @@hasInstancemetodo sul secondo operando, quando definito:
1 instanceof { [Symbol.hasInstance]: greet } // triggers greet