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 applyo call.
f.apply(this);
f.call(this);
Pero estos requieren paréntesis applyy callnos 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 setTimeoutsin 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
greetsin paréntesis:1. Como constructor
Con
newusted puede invocar una función sin paréntesis:De MDN en el
newoprador :2. Como
toStringovalueOfImplementacióntoStringyvalueOfson métodos especiales: se les llama implícitamente cuando es necesaria una conversión:Podrías (ab) usar este patrón para llamar
greetsin paréntesis:O con
valueOf:valueOfytoStringde hecho son llamados desde el método @@ toPrimitive (desde ES6), por lo que también puede implementar ese método:2.b Anulación
valueOfde prototipo de funciónPodría tomar la idea anterior para anular el
valueOfmétodo en elFunctionprototipo :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...ofsintaxis.Primero necesitamos una variante de generador de la
greetfunción original :Y luego lo llamamos sin paréntesis definiendo el método de iterador @@ :
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 :
o una
for ... ofconstrucción, pero tiene paréntesis propios: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 quegreetse haya ejecutado (probado en FF y Chrome). Puede gestionar la excepción con untry...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 suObject.definePropertylugar.Necesitamos crear una variante de la
greetfunción original para esto:Y entonces:
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: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
instanceofoperador ejecuta el@@hasInstancemétodo en el segundo operando, cuando se define:fuente
valueOf.func``;'1' |> alertLa forma más fácil de hacerlo es con el
newoperador:Si bien eso es poco ortodoxo y antinatural, funciona y es perfectamente legal.
El
newoperador no requiere paréntesis si no se utilizan parámetros.fuente
!fda 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:!foovuelvefalsePuedes 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.definePropertylugar.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.
locationyjavascript:protocolo:Una de esas técnicas es abusar del
javascript:protocolo en lalocationasignación.Ejemplo de trabajo
Aunque técnicamente
\x28y\x29aú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.
onerroryeval:De manera similar, dependiendo del navegador, podemos abusar de lo global
onerror, configurándoloevaly 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 definaUncaughty 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
evaly escribir la cadena sin usar los caracteres entre paréntesis?evalnormalmente requiere paréntesis, por lo tanto, este truco de evasión de filtro.\x28y\x29todavía son paréntesis.'\x28' === '('.En ES6, tiene lo que se llama Tagged Template Literals .
Por ejemplo:
fuente