español ejemplos descargar definicion curso caracteristicas javascript

ejemplos - javascript html



¿Por qué una evaluación no ejecutada tiene un efecto en el comportamiento en algunos navegadores? (3)

TL; DR

La causa probable es la interacción de los arguments function.arguments no estándar con las optimizaciones del navegador para el código de función que contiene eval y / o arguments . Sin embargo, solo las personas familiarizadas con los detalles de la implementación en cada navegador podrán explicar el por qué en profundidad.

El principal problema aquí parece ser el uso de Function.prototype.arguments no estándar. Cuando no lo usas, el extraño comportamiento desaparece.

La especificación solo menciona el objeto de arguments , y nunca dice que pueda tratarse como una propiedad, con el prefijo [funcName]. . No estoy seguro de dónde provino, pero es probable que sea algo anterior a ES3, que se mantiene en los navegadores para la compatibilidad con versiones anteriores. Como dice la respuesta de Cory, ese uso ahora se desaconseja en MDN . MSDN , sin embargo, no dice nada en contra. También encontré que se menciona en esta especificación para la compatibilidad entre navegadores * , que no parece ser implementado de manera consistente por los proveedores (ningún navegador pasa todas las tests ). Además, el uso de arguments como una propiedad de la función no está permitido en modo estricto (de nuevo, eso no está en las especificaciones de ECMA, e IE9 parece ignorar la restricción).

Luego vienen eval y arguments . Como sabe , la especificación ECMAScript requiere que se realicen algunas operaciones extra para que se puedan usar esas construcciones de lenguaje (en el caso de eval , la operación es diferente dependiendo de si la llamada es direct o no). Dado que esas operaciones pueden tener un impacto en el rendimiento, (¿algunos?) Los motores de JavaScript realizan optimizaciones para evitarlas si no se usan eval o arguments . Esas optimizaciones, combinadas con el uso de una propiedad no estándar del objeto Function , parecen ser lo que está causando los resultados extraños que obtuviste. Desafortunadamente, no conozco los detalles de la implementación de cada navegador, por lo que no puedo darle una respuesta precisa sobre por qué vemos esos efectos colaterales.

(*) Especificación escrita por un usuario SO , por cierto.

Pruebas

Realicé algunas pruebas para ver cómo interactúan eval (llamadas directas e indirectas), arguments y fn.arguments en IE, Firefox y Chrome. No es sorprendente que los resultados varíen en cada navegador, ya que estamos tratando con los fn.arguments no estándar.

La primera prueba solo verifica la igualdad estricta de los arguments y arguments de fn.arguments , y si la presencia de eval afecta eso de alguna manera. Inevitablemente, mis pruebas de Chrome están marcadas por la presencia de arguments , lo que tiene un efecto en los resultados, como dijiste en la pregunta. Aquí están los resultados:

| no eval | direct eval call | indirect eval call -----------------------+-----------+--------------------+--------------------- IE 9.0.8112.16421 | true | true | true FF 16.0.2 | false | false | false Chrome 22.0.1229.94 | true | false | true

Puedes ver que IE y Firefox son más consistentes: los objetos son siempre iguales en IE y nunca iguales en Firefox. En Chrome, sin embargo, solo son iguales si el código de función no contiene una llamada de eval directa.

Las pruebas restantes son pruebas de asignación basadas en funciones que se parecen a las siguientes:

function fn(x) { // Assignment to x, arguments[0] or fn.arguments[0] console.log(x, arguments[0], fn.arguments[0]); return; // make sure eval is not actually called // No eval, eval(""), or (1,eval)("") }

A continuación se muestran los resultados de cada navegador probado.

Internet Explorer 9.0.8112.16421

| no eval | direct eval call | indirect eval call -----------------------------+---------------------------+---------------------------+-------------------------- arguments[0] = ''changed''; | changed, changed, changed | changed, changed, changed | changed, changed, changed x = ''changed''; | changed, changed, changed | changed, changed, changed | changed, changed, changed fn.arguments[0] = ''changed''; | changed, changed, changed | changed, changed, changed | changed, changed, changed

En primer lugar, parece que mis pruebas de IE dan resultados diferentes a los que se indican en la pregunta; Siempre me "cambian" en IE. Tal vez usamos diferentes compilaciones de IE? De todos modos, lo que muestran los resultados anteriores es que IE es el navegador más consistente. Como en los arguments === fn.arguments IE arguments === fn.arguments siempre es verdadero, x , arguments[0] o function.arguments[0] todos apuntan al mismo valor. Si cambia alguno de ellos, los tres emitirán el mismo valor modificado.

Firefox 16.0.2

| no eval | direct eval call | indirect eval call -----------------------------+------------------------------+---------------------------+----------------------------- arguments[0] = ''changed''; | changed, changed, original | changed, changed, changed | changed, changed, original x = ''changed''; | changed, changed, original | changed, changed, changed | changed, changed, original fn.arguments[0] = ''changed''; | original, original, original | changed, changed, changed | original, original, original

Firefox 16.0.2 es menos consistente: aunque los arguments nunca son === fn.arguments en Firefox, eval tiene un efecto en las asignaciones. Sin una llamada directa a eval , cambiar los arguments[0] cambia x , pero no cambia fn.arguments[0] . Cambiar fn.arguments[0] no cambia x ni arguments[0] . Fue una completa sorpresa que el cambio de fn.arguments[0] no se cambie por sí mismo.

Cuando se introduce eval("") , el comportamiento es diferente: al cambiar uno de x , los arguments[0] o function.arguments[0] comienzan a afectar a los otros dos. Así que es como si los arguments se === function.arguments en === function.arguments - excepto que no lo hace, Firefox todavía dice que los arguments === function.arguments son false . Cuando se usa una llamada eval indirecta en su lugar, Firefox se comporta como si no hubiera una eval .

Cromo 22.0.1229.94

| no eval | direct eval call | indirect eval call -----------------------------+----------------------------+------------------------------+-------------------------- arguments[0] = ''changed''; | changed, changed, changed | changed, changed, original | changed, changed, changed x = ''changed''; | changed, changed, changed | changed, changed, original | changed, changed, changed fn.arguments[0] = ''changed''; | changed, changed, changed | original, original, original | changed, changed, changed

El comportamiento de Chrome es similar al de Firefox: cuando no hay una eval o una llamada de eval indirecta, se comporta de manera consistente. Con la llamada a eval directa, el vínculo entre los arguments y fn.arguments parece romperse (lo que tiene sentido, considerando los arguments === fn.arguments es false cuando eval("") está presente). Chrome también presenta el caso extraño de que fn.arguments[0] sea original incluso después de la asignación, pero sucede cuando está presente eval("") (mientras que en Firefox ocurre cuando no hay eval o con la llamada indirecta).

Aquí está el código completo para las pruebas, si alguien quiere ejecutarlas. También hay una versión en vivo en jsfiddle .

function t1(x) { console.log("no eval: ", arguments === t1.arguments); } function t2(x) { console.log("direct eval call: ", arguments === t2.arguments); return; eval(""); } function t3(x) { console.log("indirect eval call: ", arguments === t3.arguments); return; (1, eval)(""); } // ------------ function t4(x) { arguments[0] = ''changed''; console.log(x, arguments[0], t4.arguments[0]); } function t5(x) { x = ''changed''; console.log(x, arguments[0], t5.arguments[0]); } function t6(x) { t6.arguments[0] = ''changed''; console.log(x, arguments[0], t6.arguments[0]); } // ------------ function t7(x) { arguments[0] = ''changed''; console.log(x, arguments[0], t7.arguments[0]); return; eval(""); } function t8(x) { x = ''changed''; console.log(x, arguments[0], t8.arguments[0]); return; eval(""); } function t9(x) { t9.arguments[0] = ''changed''; console.log(x, arguments[0], t9.arguments[0]); return; eval(""); } // ------------ function t10(x) { arguments[0] = ''changed''; console.log(x, arguments[0], t10.arguments[0]); return; (1, eval)(""); } function t11(x) { x = ''changed''; console.log(x, arguments[0], t11.arguments[0]); return; (1, eval)(""); } function t12(x) { t12.arguments[0] = ''changed''; console.log(x, arguments[0], t12.arguments[0]); return; (1, eval)(""); } // ------------ console.log("--------------"); console.log("Equality tests"); console.log("--------------"); t1(''original''); t2(''original''); t3(''original''); console.log("----------------"); console.log("Assignment tests"); console.log("----------------"); console.log(''no eval''); t4(''original''); t5(''original''); t6(''original''); console.log(''direct call to eval''); t7(''original''); t8(''original''); t9(''original''); console.log(''indirect call to eval''); t10(''original''); t11(''original''); t12(''original'');

Supongamos que tengo estas dos funciones:

function change(args) { args[0] = "changed"; return " => "; } function f(x) { return [x, change(f.arguments), x]; } console.log(f("original"));

En la mayoría de los navegadores, excepto Opera, esto devuelve ["original", " => ", "original"] .

Pero si cambio la función f como esta,

function f(x) { return [x, change(f.arguments), x]; eval(""); }

devolverá ["original", " => ", "changed"] en IE9, Safari 5 y Firefox 16 y 17.

Si sustituyo eval("") por arguments , también cambiará en Chrome.

Puedes probarlo en tu propio navegador en jsFiddle.

No entiendo este comportamiento en absoluto. Si la función vuelve antes de que se ejecuten esas declaraciones, ¿cómo pueden esas declaraciones afectar el valor de retorno? Incluso si las declaraciones se ejecutaran, ¿por qué tendrían algún efecto en la mutación del argumento?


Aquí hay algunos matices de Javascript excelentes que entrarán en vigencia:

change(f.arguments) change(x)

El primero pasa la lista de argumentos a change () como referencia . Los arreglos tienden a ser referencias en Javascript. Esto significa que si cambia un elemento de una matriz en otro lugar, esos cambios se aplicarán donde sea que use esa misma matriz .

El último pasa el argumento x como un valor . Es como entregar una copia: el cambio puede cambiarlo y solo afectará a la variable local. Como x es una cadena y las cadenas son inmutables, args [0] = "cambiado" en la función change () no hace nada. Intenta lo siguiente en una consola:

var x = "asdf"; x[0] = "foo"; console.log(x); // should print "asdf"

En las funciones f, h, g, el valor de los argumentos [0] se cambia en el segundo índice en la lista devuelta. El tercer índice devolverá "cambiado".

Teóricamente Sin embargo, algunos navegadores compilan Javascript, lo que provoca condiciones de tipo de carrera y es posible que las instrucciones no se ejecuten en el orden en que se escriben, especialmente si están en la misma línea y está cambiando la pila y accediendo a ella desde la misma línea.

return [x, change(f.arguments), x];

... intenta cambiar la variable de argumentos y acceder a x (que es un argumento) al mismo tiempo. En Chrome, por ejemplo, pasar f.arguments to change () da como resultado ["original", "=>", "original"] mientras que pasar argumentos justos da como resultado ["original", "=>", "change"] . También puede ser un problema de alcance y cómo Javascript maneja el valor y los tipos de referencia, pero ese comportamiento es diferente en todos los navegadores.

No vi ningún comportamiento extraño con eval () dado lo que he descrito, pero parece que indicar los argumentos en la función h () después de la devolución crea un efecto secundario que sospecho que está causado por la compilación de Javascript de Chrome. Lo que es realmente interesante es que internamente, el shell ejecuta una variable devolviendo su valor, pero no se está escribiendo en ningún lugar, tal vez se espera en un caché. Es difícil decir lo que está pasando en la pila de Javascript, pero lo que estás haciendo es ciertamente poco convencional y seguramente arruinará el compilador en todos los navegadores.

EDITAR:

Aún mejor: console.log (h.arguments); devuelve [x, cambio (argumentos), x]; argumentos

se registrará

["changed"] ["original", " => ", "changed"]

¡Seguro que se parece a una condición de carrera, o un pasaje torcido de referencias a la matriz de argumentos dentro de las funciones!


Solo jugando, encontré que quitas la f. desde el valor de f.arguments en la matriz, y solo use arguments , el comportamiento es el mismo sin importar lo que venga después de la return :

function f(x) { return [x, change(arguments), x]; } function g(x) { return [x, change(arguments), x]; eval(""); } function h(x) { return [x, change(arguments), x]; arguments; }

En los tres casos que usan x = "original" , la salida es:

["original", " => ", "changed"] ["original", " => ", "changed"] ["original", " => ", "changed"]

En este caso, los valores son modificados por change() como si la matriz de arguments se pasara por referencia. Para mantener "original" sin cambios , podría sugerirle que primero convierta los arguments objeto a una matriz real (por lo tanto, pase los elementos de arguments por valor ):

function h(x) { var argsByValue = Array.prototype.slice.call(arguments, 0); return [x, change(argsByValue), x]; }

En el ejemplo anterior, x permanecerá "original" antes y después del change() , porque se modificó una copia de x , no la original.

Todavía no estoy seguro de cuáles son los efectos de tener eval(""); o arguments; son, pero su pregunta sigue siendo interesante, como son los resultados.

Lo que es realmente extraño es que esto incluso afecta a poner el change() en su propio ámbito de función con una copia de los argumentos de la función

function f(x) { return ((function(args) { return [x, change(args), x]; })(f.arguments)); // the presence of the line below still alters the behavior arguments; }

Parece que una referencia a f.arguments todavía se mantiene en este caso. Cosas raras.

Actualizaciones

Desde MDN :

El objeto de arguments es una variable local disponible dentro de todas las funciones; arguments como una propiedad de la Function ya no se pueden usar.

Parece que, al menos para Firefox, no deberías usar arguments como una propiedad (por ejemplo, function foo() { var bar = foo.arguments; } ), aunque no dicen por qué.