Hay varias formas diferentes de llamar a una función sin paréntesis.
Supongamos que tiene esta función definida:
function greet() {
console.log('hello');
}
A continuación, siga algunas formas de llamar greetsin paréntesis:
1. Como constructor
Con newusted puede invocar una función sin paréntesis:
new greet; // parentheses are optional in this construct.
De MDN en el newoprador :
Sintaxis
new constructor[([arguments])]
2. Como toStringo valueOfImplementación
toStringy valueOfson métodos especiales: se les llama implícitamente cuando es necesaria una conversión:
var obj = {
toString: function() {
return 'hello';
}
}
'' + obj; // concatenation forces cast to string and call to toString.
Podrías (ab) usar este patrón para llamar greetsin paréntesis:
'' + { toString: greet };
O con valueOf:
+{ valueOf: greet };
valueOfy toStringde hecho son llamados desde el método @@ toPrimitive (desde ES6), por lo que también puede implementar ese método:
+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }
2.b Anulación valueOfde prototipo de función
Podría tomar la idea anterior para anular el valueOfmétodo en el Functionprototipo :
Function.prototype.valueOf = function() {
this.call(this);
// Optional improvement: avoid `NaN` issues when used in expressions.
return 0;
};
Una vez que haya hecho eso, puede escribir:
+greet;
Y aunque hay paréntesis involucrados en la línea, la invocación de activación real no tiene paréntesis. Vea más sobre esto en el blog "Métodos de llamada en JavaScript, sin realmente llamarlos"
3. Como generador
Podría definir una función generadora (con *), que devuelve un iterador . Puede llamarlo usando la sintaxis de propagación o con la for...ofsintaxis.
Primero necesitamos una variante de generador de la greetfunción original :
function* greet_gen() {
console.log('hello');
}
Y luego lo llamamos sin paréntesis definiendo el método de iterador @@ :
[...{ [Symbol.iterator]: greet_gen }];
Normalmente los generadores tendrían una yieldpalabra clave en alguna parte, pero no es necesaria para que se llame a la función.
La última declaración invoca la función, pero eso también podría hacerse con la desestructuración :
[,] = { [Symbol.iterator]: greet_gen };
o una for ... ofconstrucción, pero tiene paréntesis propios:
for ({} of { [Symbol.iterator]: greet_gen });
Tenga en cuenta que también puede hacer lo anterior con la greetfunción original , pero provocará una excepción en el proceso, después de que greet se haya ejecutado (probado en FF y Chrome). Puede gestionar la excepción con un try...catchbloque.
4. Como Getter
@ jehna1 tiene una respuesta completa sobre esto, así que dale crédito. Aquí hay una manera de llamar a una función sin paréntesis en el ámbito global, evitando el método obsoleto__defineGetter__ . Utiliza en su Object.definePropertylugar.
Necesitamos crear una variante de la greetfunción original para esto:
Object.defineProperty(window, 'greet_get', { get: greet });
Y entonces:
greet_get;
Reemplace windowcon cualquiera que sea su objeto global.
Puede llamar a la greetfunción original sin dejar un rastro en el objeto global como este:
Object.defineProperty({}, 'greet', { get: greet }).greet;
Pero uno podría argumentar que tenemos paréntesis aquí (aunque no están involucrados en la invocación real).
5. Como función de etiqueta
Desde ES6 puede llamar a una función pasándole un literal de plantilla con esta sintaxis:
greet``;
Consulte "Literales de plantillas etiquetadas" .
6. Como controlador proxy
Desde ES6, puede definir un proxy :
var proxy = new Proxy({}, { get: greet } );
Y luego leer cualquier valor de propiedad invocará greet:
proxy._; // even if property not defined, it still triggers greet
Hay muchas variaciones de esto. Un ejemplo más:
var proxy = new Proxy({}, { has: greet } );
1 in proxy; // triggers greet
7. Como verificador de instancias
El instanceofoperador ejecuta el @@hasInstancemétodo en el segundo operando, cuando se define:
1 instanceof { [Symbol.hasInstance]: greet } // triggers greet