Hoy me dijeron que es posible invocar una función sin paréntesis. Las únicas formas en que podía pensar era usando funciones como apply
o call
.
f.apply(this);
f.call(this);
Pero estos requieren paréntesis apply
y call
nos dejan en el punto de partida. También consideré la idea de pasar la función a algún tipo de controlador de eventos como setTimeout
:
setTimeout(f, 500);
Pero entonces la pregunta se convierte en "¿cómo invocar setTimeout
sin paréntesis?"
Entonces, ¿cuál es la solución a este acertijo? ¿Cómo se puede invocar una función en Javascript sin usar paréntesis?
javascript
Mike Cluck
fuente
fuente
<insert any solution>
es objetivamente mejor o más útil quef()
.Respuestas:
Hay varias formas diferentes de llamar a una función sin paréntesis.
Supongamos que tiene esta función definida:
A continuación, siga algunas formas de llamar
greet
sin paréntesis:1. Como constructor
Con
new
usted puede invocar una función sin paréntesis:De MDN en el
new
oprador :2. Como
toString
ovalueOf
ImplementacióntoString
yvalueOf
son métodos especiales: se les llama implícitamente cuando es necesaria una conversión:Podrías (ab) usar este patrón para llamar
greet
sin paréntesis:O con
valueOf
:valueOf
ytoString
de hecho son llamados desde el método @@ toPrimitive (desde ES6), por lo que también puede implementar ese método:2.b Anulación
valueOf
de prototipo de funciónPodría tomar la idea anterior para anular el
valueOf
método en elFunction
prototipo :Una vez que haya hecho eso, puede escribir:
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 lafor...of
sintaxis.Primero necesitamos una variante de generador de la
greet
función original :Y luego lo llamamos sin paréntesis definiendo el método de iterador @@ :
Normalmente los generadores tendrían una
yield
palabra 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 :
o una
for ... of
construcción, pero tiene paréntesis propios:Tenga en cuenta que también puede hacer lo anterior con la
greet
función original , pero provocará una excepción en el proceso, después de quegreet
se haya ejecutado (probado en FF y Chrome). Puede gestionar la excepción con untry...catch
bloque.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 suObject.defineProperty
lugar.Necesitamos crear una variante de la
greet
función original para esto:Y entonces:
Reemplace
window
con cualquiera que sea su objeto global.Puede llamar a la
greet
función original sin dejar un rastro en el objeto global como este: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:
Consulte "Literales de plantillas etiquetadas" .
6. Como controlador proxy
Desde ES6, puede definir un proxy :
Y luego leer cualquier valor de propiedad invocará
greet
:Hay muchas variaciones de esto. Un ejemplo más:
7. Como verificador de instancias
El
instanceof
operador ejecuta el@@hasInstance
método en el segundo operando, cuando se define:fuente
valueOf
.func``;
'1' |> alert
La forma más fácil de hacerlo es con el
new
operador:Si bien eso es poco ortodoxo y antinatural, funciona y es perfectamente legal.
El
new
operador no requiere paréntesis si no se utilizan parámetros.fuente
!f
da como resultado lo mismo, sin instanciar una instancia de la función constructora (que requiere crear un nuevo contexto). Es mucho más eficiente y se usa en muchas bibliotecas populares (como Bootstrap).+
,-
,~
) en este tipo de bibliotecas, es salvar un byte en la versión minimizada de la biblioteca en la que no se necesita el valor de retorno. (es decir!function(){}()
) La sintaxis de auto-llamada habitual es(function(){})()
(desafortunadamente, la sintaxisfunction(){}()
no es de navegador cruzado) Dado que la función de auto-llamada más alta generalmente no tiene nada que devolver, generalmente es el objetivo de esta técnica de guardado de bytesfunction foo() { alert("Foo!") };
Por ejemplo:!foo
vuelvefalse
Puedes usar getters y setters.
Ejecute este script solo con:
Editar:
¡Incluso podemos hacer argumentos!
Edición 2:
También puede definir funciones globales que se pueden ejecutar sin paréntesis:
Y con argumentos:
Descargo de responsabilidad:
Como dijo @MonkeyZeus: nunca usarás este código en producción, sin importar cuán buenas sean tus intenciones.
fuente
__defineGetter__
está en desuso. Usar en suObject.defineProperty
lugar.Aquí hay un ejemplo para una situación particular:
Aunque esa declaración en realidad no se invoca, dará lugar a una futura invocación .
Pero, creo que las áreas grises podrían estar bien para acertijos como este :)
fuente
Si aceptamos un enfoque de pensamiento lateral, en un navegador hay varias API que podemos abusar para ejecutar JavaScript arbitrario, incluida la llamada a una función, sin ningún paréntesis real.
1.
location
yjavascript:
protocolo:Una de esas técnicas es abusar del
javascript:
protocolo en lalocation
asignación.Ejemplo de trabajo
Aunque técnicamente
\x28
y\x29
aún son paréntesis una vez que se evalúa el código, el carácter real(
y el)
carácter no aparecen. Los paréntesis se escapan en una cadena de JavaScript que se evalúa en la asignación.2.
onerror
yeval
:De manera similar, dependiendo del navegador, podemos abusar de lo global
onerror
, configurándoloeval
y arrojando algo que se convertirá en cadena a JavaScript válido. Este es más complicado, porque los navegadores son inconsistentes en este comportamiento, pero aquí hay un ejemplo para Chrome.Ejemplo de trabajo para Chrome (no Firefox, otros no probados):
Esto funciona en Chrome porque
throw'test'
pasará'Uncaught test'
como primer argumento aonerror
, que es JavaScript casi válido. Si en cambio lo hacemosthrow';test'
, pasará'Uncaught ;test'
. ¡Ahora tenemos JavaScript válido! Simplemente definaUncaught
y reemplace la prueba con la carga útil.En conclusión:
Tal código es realmente horrible , y nunca debe usarse , pero a veces se usa en ataques XSS, por lo que la moraleja de la historia es no confiar en el filtrado de paréntesis para prevenir XSS. Usar un CSP para prevenir dicho código también sería una buena idea.
fuente
eval
y escribir la cadena sin usar los caracteres entre paréntesis?eval
normalmente requiere paréntesis, por lo tanto, este truco de evasión de filtro.\x28
y\x29
todavía son paréntesis.'\x28' === '('
.En ES6, tiene lo que se llama Tagged Template Literals .
Por ejemplo:
fuente