una - pasar variables entre funciones javascript
Invocar una función sin paréntesis (6)
Aquí hay un example para una situación particular:
window.onload = funcRef;
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 :)
Hoy me dijeron que es posible invocar una función sin paréntesis.
Las únicas formas en que podía pensar era en usar funciones como
apply
o
call
.
f.apply(this);
f.call(this);
Pero estos requieren paréntesis en
apply
y
call
dejándonos 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?
En ES6, tiene lo que se llama Tagged Template Literals .
Por ejemplo:
function foo(val) {
console.log(val);
}
foo`Tagged Template Literals`;
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'');
}
Luego, aquí hay algunas formas de llamar a
greet
sin paréntesis:
1. Como constructor
Con
new
puedes invocar una función sin paréntesis:
new greet; // parentheses are optional in this construct.
Sintaxis
new constructor[([arguments])]
2. En cuanto a la
toString
o el valor de la implementación
toString
y
valueOf
son 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 a
greet
sin paréntesis:
'''' + { toString: greet };
O con
valueOf
:
+{ valueOf: greet };
2.b Anulación del valor de prototipo de función
Podría tomar la idea anterior para anular el método
valueOf
en el
prototipo de
Function
:
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
iterator
.
Puede llamarlo utilizando la
sintaxis de propagación
o con el
for...of
sintaxis.
Primero necesitamos una variante de generador de la función de
greet
original:
function* greet_gen() {
console.log(''hello'');
}
Y luego lo llamamos sin paréntesis:
[...{ [Symbol.iterator]: greet_gen }];
Normalmente los generadores tendrían una palabra clave de
yield
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 destructuring :
[,] = { [Symbol.iterator]: greet_gen };
o un
for ... of
construcción, pero tiene paréntesis propios:
for ({} of { [Symbol.iterator]: greet_gen });
Tenga en cuenta que también
puede
hacer lo anterior con la función de
greet
original, pero provocará una excepción en el proceso,
después de que
se haya ejecutado el
greet
(probado en FF y Chrome).
Podrías gestionar la excepción con un
try...catch
block.
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
__defineGetter__
desuso
.
Utiliza
Object.defineProperty
en
Object.defineProperty
lugar.
Necesitamos crear una variante de la función de
greet
original para esto:
Object.defineProperty(window, ''greet_get'', { get: greet });
Y entonces:
greet_get;
Reemplace la
window
con cualquiera que sea su objeto global.
Puede llamar a la función de
greet
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
Con 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
En 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
La forma más fácil de hacerlo es con el
new
operador:
function f() {
alert(''hello'');
}
new f;
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.
Puedes usar getters y setters.
var h = {
get ello () {
alert("World");
}
}
Ejecute este script solo con:
h.ello // Fires up alert "world"
Editar:
¡Incluso podemos hacer argumentos!
var h = {
set ello (what) {
alert("Hello " + what);
}
}
h.ello = "world" // Fires up alert "Hello world"
Edición 2:
También puede definir funciones globales que se pueden ejecutar sin paréntesis:
window.__defineGetter__("hello", function() { alert("world"); });
hello; // Fires up alert "world"
Y con argumentos:
window.__defineSetter__("hello", function(what) { alert("Hello " + what); });
hello = "world"; // Fires up alert "Hello world"
Renuncia:
Como dijo @MonkeyZeus: nunca usarás este código en producción, sin importar cuán buenas sean tus intenciones.
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
y
javascript:
protocolo:
Una de esas técnicas es abusar del protocolo
javascript:
en
location
asignación de
location
.
Ejemplo de trabajo
location=''javascript:alert/x281/x29''
Aunque
técnicamente
/x28
y
/x29
siguen siendo paréntesis una vez que se evalúa el código, el carácter real
(
y
)
no aparece.
Los paréntesis se escapan en una cadena de JavaScript que se evalúa en la asignación.
2.
onerror
y
eval
:
De manera similar, dependiendo del navegador, podemos abusar del
onerror
global, configurándolo como
eval
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):
window.onerror=eval;Uncaught=0;throw'';alert/x281/x29'';
Esto funciona en Chrome porque
throw''test''
pasará
''Uncaught test''
como primer argumento para
onerror
, que es JavaScript casi válido.
Si en su lugar lanzamos
throw'';test''
, pasará
''Uncaught ;test''
.
¡Ahora tenemos JavaScript válido!
Simplemente defina Sin
Uncaught
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.