2012-02-21 14 views
43

Un amigo mío descubrió un comportamiento interesante en algunos códigos Javascript, que decidí investigar más a fondo.¿Una función es más grande que una matriz?

La comparación

(function (x) {return x*x;}) > [1,2,3] 

vuelve true en la mayoría de los principales navegadores (Firefox, Chrome, Opera y Safari) y false en IE9. Para mí, no hay un resultado lógico de esta comparación que no sea undefined, ya que no hay forma de decir que una función es mayor que una matriz.

Al leer esto en el estándar ECMA-script, dice que los argumentos reales de > cuando se usan en objetos son el resultado de llamar a la operación interna ToNumber en los argumentos. Algunos experimentos y lecturas adicionales me dicen que esto no es lo mismo que aplicar una conversión de tipo como (Number) arg. Al leer la especificación, me cuesta entender qué está pasando aquí.

¿Alguien puede informarme sobre lo que realmente está sucediendo aquí?

+5

, por supuesto, la función es más grande que la matriz, que puede producir matrices heckuvalotta ;-) –

+5

No han leído lo suficiente de la especificación todavía. El operador '>' también puede comparar cadenas. – Pointy

+1

@Pointy, de hecho. 'f' es mayor que '1' (¿hay un significado oculto?). –

Respuesta

54

Los operandos a > no se convierten necesariamente en números. El abstract relational comparison algorithm llama a ToPrimitive con la sugerencia Number, pero ToPrimitive aún puede devolver una cadena (y en el caso de ambas funciones y matrices, lo hace).

Así que terminas comparando dos cadenas. El resultado de llamar al toString on function objects no está definido por la especificación, aunque la mayoría de los motores principales devuelven el código fuente de la función (o alguna forma de ella, y el formato varía). El resultado de llamar al toString on arrays es el mismo que join.

así que las probabilidades son que usted va a terminar haciendo básicamente esto:

"function (x) {return x*x;}" > "1,2,3" 

Puesto que la forma exacta de la cadena para la función puede variar de un navegador a un navegador (y tenga en cuenta Esailija's investigations   — miradas como IE9 mantiene el exterior (), Chrome no), no es demasiado sorprendente que el resultado pueda variar.

+0

Creo que las matrices se convierten en cadenas a través de '.join()' ... – Pointy

+1

También podría ser 'function() {...' y '1,2,3' (creo que eso es lo que Chrome hará de ellas). –

+0

@Pointy: Sí, y 'toString' en funciones devuelve la fuente (generalmente, no garantizado). Estaba pensando en 'Object.prototype.toString.call (func)' - doh! –

60

En IE < 9, .toString ing (function (x) {return x*x;}) da

"(function (x) {return x*x;})" 

Mientras que en cromo le da:

"function (x) {return x*x;}" 

Si se compara:

"function (x) {return x*x;}" > "1,2,3" // true 
"(function (x) {return x*x;})" > "1,2,3" // false 

que es efectivamente el mismo que comparando:

"f" > "1" 
"(" > "1" 

¿Qué es la misma que la comparación:

102 > 49 
40 > 49 

Así que esa es la forma en que obtenemos de una comparación función y la matriz a una simple comparación número :)

+0

Interesante análisis de incompatibilidad obvia, permitido por la especificación. – Abel

+0

No en mi IE9: http://i.imgur.com/1zhLa.png. Solo veo los parens en el modo IE8/IE7. ¿Tu navegador está en modo de compatibilidad? – gilly3

+0

@ gilly3 cierto que tenía el modo IE7, el OP debe haber tenido el mismo error, ya que devuelve cierto para la comparación (igual que los navegadores modernos) – Esailija

2

IE y otros navegadores utilizarán el misma cadena de comparación para ambos objetos.La razón de esta diferencia es IE convertir la función en la cadena literal como entró:

(function (x) {return x*x;}) 

Otros navegadores (pruebas en Firefox) emitirán su propia interpretación compilado de la función:

function (x) { 
    return x * x; 
} 

Desde el primer carácter de la representación de función de IE es (, que es más alto que 1, devolverá falso. Como f es inferior a 1, otros navegadores devolverán verdadero.

5

Vamos a profundizar en la especificación de ECMA. He incluido los números de la sección para que pueda hacer referencia.

11.8.2 El operador mayor que (>)

El RelationalExpression producción: RelationalExpression> ShiftExpression se evalúa de la siguiente manera:

  1. Let Lref ser el resultado de la evaluación de RelationalExpression .
  2. Deje que lval sea GetValue (lref).
  3. Deje que rref sea el resultado de evaluar ShiftExpression.
  4. Deje que rval sea GetValue (rref).
  5. Sea r el resultado de realizar comparación relacional abstracta rval < lval con LeftFirst igual a falso. (ver 11.8.5).

La parte importante de ello es la Abstract Relational Comparación. Que se define:

11.8.5 El resumen relacional Comparación Algoritmo

La función toPrimitive primero se pidió a los objetos. Aunque esto está sesgado para devolver números si es posible, también se pueden derivar cadenas. Una vez que esto ha ocurrido, se examinará lo siguiente:

a. Si py es un prefijo de px, devuelve falso. (Un valor de cadena p es un prefijo de valor de cadena q si q puede ser el resultado de concatenación p y alguna otra cadena. Observe que cualquier cadena es un prefijo de sí mismo, porque r puede ser la cadena vacía.)

b. Si px es un prefijo de py, devuelve true.

c. Deje k sea el número entero no negativo más pequeño tal que el carácter en la posición k dentro de px sea diferente del carácter en la posición k dentro de py. (Debe haber tal k, porque ninguna de las cadenas es un prefijo de la otra.)

d. Deje m ser el entero que es el valor de la unidad de código para el personaje en la posición k dentro de px. mi. Sea n el entero que es el valor de la unidad de código para el personaje en la posición k dentro de py. F. Si m < n, devuelve verdadero. De lo contrario, devuelve falso.

Esto significa que se examinará el primer carácter en la Cadena que sea diferente de la otra. Como ha sido señalado por Esailija, la función toString() de IE devuelve una cadena ligeramente diferente a la de los otros navegadores, lo que da como resultado una comparación diferente que tiene lugar.

Esta diferencia entre los navegadores parece ser válida como se dice aquí:

15.2.4.4 Object.valueOf()

Cuando el método valueOf se le llama, los siguientes pasos se toman:

  1. Sea O el resultado de llamar a ToObject pasando este valor como argumento.
  2. Si O es el resultado de llamar al constructor de objetos con un objeto host (15.2.2.1), entonces a. Devuelva O u otro valor como el objeto host pasado originalmente al constructor. El resultado específico que se devuelve está definido por la implementación.
  3. Volver O.
Cuestiones relacionadas