2011-01-28 11 views
8

Me enfrenté a esta extraña situación donde foreach como constructo de javascript no funciona en IE pero funciona en FF. Bueno, no todos for..in simplemente esta funciton especial no funciona. Voy a publicar el código. Probado en IE8. Probado también con XHTML DTD.Javascript for..in bucles sobre argumentos ie.for (arg en argumentos) no funciona en IE8 pero funciona en Chrome 8

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
    <HEAD> 
    <TITLE> Test </TITLE> 
    <META NAME="Generator" CONTENT="EditPlus"> 
    <META NAME="Author" CONTENT=""> 
    <META NAME="Keywords" CONTENT=""> 
    <META NAME="Description" CONTENT=""> 
    </HEAD> 
<script type="text/javascript"> 
<!-- 
    String.prototype.format = function() {   
    var formatted = this;  
    var mycars = new Array(); //some 
    mycars[0] = "Saab"; 
    mycars[1] = "Volvo"; 
    mycars[2] = "BMW"; 
    var arg; 
    for (arg in mycars) {  alert('it comes here');  
     formatted = formatted.replace("{" + arg + "}", arguments[arg]);   }   
     return formatted; 
    }; 

    String.prototype.format2 = function() {   
    var formatted = this;  
    var arg; 
    for (arg in arguments) {  alert('it does not come here');  
     formatted = formatted.replace("{" + arg + "}", arguments[arg]);   }   
     return formatted;  
    }; 

function fn() { 
    var s = 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); 
    alert('format:'+s); 
    var s2 = 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format2('ASP', 'PHP'); 
alert('format2:'+s2); //does not replace {0}s and {1}s 
} 
//--> 
</script> 
    <BODY> 
    <input type="button" value="click " onclick="fn();" /> 

    </BODY> 
</HTML> 

actualización He publicado una pregunta equivocada que funciona en Firefox, pero no en IE8, que estaba equivocado. No funciona en Firefox también. En realidad, obtuve este código de la publicación JavaScript equivalent to printf/string.format.

+4

¡Ese es el código de W3Schools! – Raynos

+0

¿Qué versión de IE estás usando? – stivlo

+0

Estoy usando IE8, FF3.6.8 y Chrome 8.0.552 –

Respuesta

23

En primer lugar, mientras que el objeto arguments disponible en una función no es una matriz, es lo suficientemente "similar a una matriz" como para que sea preferible un bucle incremental (for (var i = 0, len = arguments.length; i < len; i++) { ... }), no solo porque funciona más rápido, sino también porque evita otras trampas, una de ellas es exactamente en lo que te estás metiendo.

Para contestar realmente la pregunta de por qué el segundo bucle no funciona, es importante darse cuenta de lo que acaba de ... en bucle hace: itera a través de todos los enumerables propiedades que se encuentran en un objeto. Ahora, he puesto en negrita 2 palabras en esa declaración, porque utilicé estas dos palabras a propósito para indicar un par de matices que, si bien pueden parecer sutiles, pueden afectar drásticamente el comportamiento de tu código si no te das cuenta de lo que está sucediendo. . enfoque

En primer lugar nos dejó en todo - y me refiero a decir, no sólo las propiedades del objeto en sí, sino también potencialmente propiedades de dicho objeto ha heredado de su prototipo, o un prototipo de su prototipo, o así sucesivamente. Por este motivo, a menudo se recomienda que "guarde" cualquiera para ... en el ciclo, calificándolo de inmediato con la condición if (obj.hasOwnProperty(p)) (suponiendo que su ciclo se haya escrito for (var p in obj)).

Pero eso no es lo que te encuentras aquí. Para eso, centrémonos en esa segunda palabra, enumerable. Todas las propiedades de los objetos en JavaScript son enumerables o no enumerables, lo que se relaciona más o menos directamente con si la propiedad aparece en un for ... in loop o no. En navegadores como Firefox e IE, resulta que las propiedades numéricas del objeto arguments son no enumerables (ni tampoco es length), ¡que es precisamente por eso que no está iterando a través de nada!

Pero realmente, al final, para iterar a través de cualquier cosa que sea un Array o una matriz, es mejor usar un bucle incremental (como también lo dijo M. Kolodny), y evitar estos chanchullos por completo (sin mencionar posibles incompatibilidades entre navegadores - me parecen estar notando que en Chrome 10, las propiedades numéricas de arguments objetos son enumerable)

+0

Gracias Ken, +1 respuesta interesante, ¿sabes por qué si construyo el objeto con las llaves, las propiedades son enumerables ... entonces, qué tipo de objeto son los argumentos? O póngalo de otra manera, ¿cómo declaro un Objeto con propiedades que no son enumerables? Me siento un poco raro de tener un Objeto "argumentos" y no entenderlo, parece construido con "magia" no con los constructos de lenguaje ordinario. Pero tal vez me estoy perdiendo algo. – stivlo

+1

Bueno, 'arguments' es algo especial ya que es uno de los objetos implementados nativamente por los tiempos de ejecución de JavaScript. Si está interesado en estas características de los objetos, ECMAScript 5 le ofrece formas de controlar completamente esas cosas en sus propios objetos personalizados, pero se da cuenta de que no todos los navegadores son compatibles con ES5. Eche un vistazo a MDN para obtener más información, quizás comenzando [aquí] (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create) y continuando desde los enlaces "ver también". –

+0

Gracias. Veo ahora "la magia" como en el ejemplo: Object.defineProperty (o, "b", {value: 2, enumerable: false}); Sin embargo, como entiendo, sugieres que es mejor no usar esta sintaxis, porque no está implementado en todos los navegadores. Es extraño que decidieran definir los argumentos como no enumerables entonces, parece un descuido. – stivlo

-2

Bueno, se supone que funciona, por lo que si no es así, la respuesta es tan simple como "es otro error en IE".

Pero la verdadera pregunta aquí es por qué estás usando for ... in para iterar sobre matrices u objetos de matriz similar (como arguments) - sólo tiene que utilizar un simple bucle for lugar, que funciona en todos los navegadores:

for (var i = 0; i < arguments.length; i++) { 
    // do something with arguments[i] 
} 
+0

¿Por qué no funciona en IE! – Raynos

+0

@Raynos: dudo mucho que incluso los desarrolladores de IE puedan responder esa pregunta. – casablanca

+0

@casablance Quiero averiguarlo. ¿Puedes señalar todo lo demás que está haciendo mal en ese código de ejemplo mientras estás en eso? – Raynos

2

Trate de usar esto como una función de formato:

String.prototype.format = function() { 
    var me = this; 
    for (var i = 0; i < arguments.length; i++) 
     me = me.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]); 
    return me; 
} 

Ahora bien, esto debería funcionar:

alert('The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP')) 

DEMO

Probado y trabajando en IE

+0

Sabía que esto funciona, lo que quería saber para qué ... no funciona. –

+1

@samarjitsamanta Eso es porque arguments [] no es realmente una matriz, simplemente se ve como una. Puede convertirlo en una matriz como esta: 'args = Array.prototype.slice.call (arguments)' Obtendrá muchas otras propiedades que hereda del objeto prototipo, pero puede filtrarlo con 'hasOwnProperty()' . Mira lo que quiero decir: ** http: //jsfiddle.net/C3nWG/**. Tener la consola abierta si no quieres molestarte demasiado con ese violín –

0

Desde mi prueba, con Firefox 3.6.13 y el IE 8 no veo ninguna diferencia en el comportamiento, los dos no van en la segunda lazo.

Una diferencia entre mycars y argumentos es que mycars es una matriz, mientras que los argumentos son un objeto.

Para demostrar esto:

alert(mycars.constructor); //shows: "function Array() { [native code] }" 
alert(arguments.constructor); //shows: "function Object() { [native code] }" 

Sin embargo, con un poco de código de prueba puedo ver que "en el" trabaja por objeto y matriz

var showWithForIn = function (myArgument) { 
    for (i in myArgument) { 
     alert(myArgument[i]); 
    } 
}; 

var testArray = function() { 
    var mycars = new Array(); //some 
    mycars[0] = "Saab"; 
    mycars[1] = "Volvo"; 
    mycars[2] = "BMW"; 
    showWithForIn(mycars); 
}; 

var testObject = function() { 
    var myFriends = { 
     0: 'John', 
     1: 'Aileen' 
    }; 
    showWithForIn(myFriends); 
}; 

function testAll() { 
    testArray(); 
    testObject(); 
} 

Por lo tanto, no estoy seguro de cómo los argumentos El objeto es diferente de un Objeto que construyes con las llaves verticales. Creo que es confuso, porque en esta prueba se trabaja tanto en Array como en Object. Mientras que el "para adentro" no funciona con argumentos.

Una vez más, en todas las pruebas que no vi ninguna diferencia entre FF 3.6 e IE 8.

ACTUALIZACIÓN: Como he descubierto gracias a los comentarios de Ken, propiedades argumentos se definen como no numerable, mientras que la hora de definir las propiedades de un objeto literal se definen implícitamente como enumerable.

+0

Lamento muchísimo que funcione IE8 y FF3.6 de la misma manera. Lo intenté de nuevo ahora. Bueno, en Chrome, funciona de manera diferente. No sé por qué publiqué una pregunta incorrecta, puede que trate de editar la pregunta que pone Chrome en lugar de FF. Pero el crédito va para Ken que resolvió el enigma, los argumentos no son enumerables. –

0

Algunos navegadores son compatibles con for..in como Chrome y Firefox 4 para repetir argumentos, pero otros navegadores Don'! T ver sus parámetros mientras se itera así. Apuesto a que en estos navegadores si hiciste JSON.stringify (argumentos) el resultado sería un objeto vacío. Según la especificación de JavaScript 1.1 y los argumentos adicionales tienen un parámetro de longitud, lo que significa que puede iterarlos utilizando los bucles for (var i = 0; i < arguments.length; i++) y while(i < arguments.length).

Personalmente una vez que me quemé usando for..in para iteración de argumento, escribí una función simple para la iteración de objeto de argumento que no depende de la longitud, porque los ID siempre etiquetan en orden a los argumentos.

var eachArg = function(args, fn, start_from, end_where) { 
    var i = start_from || 0; 
    while (args.hasOwnProperty(i)) { 
     if (end_where !== undefined && i === end_where) 
      return i; 
     if (fn !== undefined) 
      fn(i, args[i]); 
     i++; 
    } 
    return i; 
}; 

Lo uso desde cuando itero los argumentos y no me falla. Más sobre esto en mi blog http://stamat.wordpress.com/iterating-arguments-in-javascript/

+0

Si está proporcionando start_from y end_where no se llama iteración http://en.wikipedia.org/wiki/Iterator_pattern. Estás huyendo del problema. Pregunté sobre el motivo de discrepancia y no una solución, y obtuve una respuesta perfecta de [Ken] (http://stackoverflow.com/users/237950/ken-franqueiro). En realidad, citar tu caso de uso cuando te quemaste usando 'for..in' podría en realidad iluminarnos más. Aprecio tu esfuerzo de todos modos, tratando de proporcionar una solución. –

+0

Gracias por señalar mis defectos. – stamat

Cuestiones relacionadas