Javascript для... в цикле по аргументам ie.for(аргументы в аргументах) не работает в IE8, но работает в Chrome 8
Я столкнулся с этой странной ситуацией, когда foreach, как конструкция javascript, не работает в IE, но работает в FF. Ну не все for..in
просто эта особая функция не работает. Я выложу код. Проверено в IE8. Протестировано также с XHTML DTD.
<!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>
Обновление Я разместил неправильный вопрос, что он работает в FireFox, но не в IE8, что было неправильно. Это не работает в FireFox тоже. На самом деле я получил этот код из пост- JavaScript, эквивалентного printf / string.format.
5 ответов
Прежде всего, пока arguments
объект, доступный внутри функции, не является массивом, он достаточно "похож на массив", так что инкрементальный цикл for (for (var i = 0, len = arguments.length; i < len; i++) { ... }
) предпочтительнее - не только потому, что он работает быстрее, но и потому, что он избегает других ловушек - одна из которых именно то, что вы попадаете.
Чтобы на самом деле ответить на вопрос, почему второй цикл не работает, важно понять, что делает цикл for... in: он перебирает все перечисляемые свойства, найденные в объекте. Теперь я выделил 2 слова в этом утверждении, потому что я использовал эти два слова целенаправленно, чтобы указать на пару нюансов, которые, хотя они могут показаться неуловимыми, могут существенно повлиять на поведение вашего кода, если вы не понимаете, что происходит,
Сначала давайте сосредоточимся на всем - под этим я имею в виду не только свойства самого объекта, но и потенциально свойства, которые указанный объект унаследовал от своего прототипа, или прототипа своего прототипа, и так далее. По этой причине очень часто рекомендуется "охранять" любой цикл for... in, немедленно дополнительно уточняя его с условием if (obj.hasOwnProperty(p))
(при условии, что ваш цикл был написан for (var p in obj)
).
Но это не то, что вы сталкиваетесь здесь. Для этого давайте сосредоточимся на этом втором слове, перечисляемом. Все свойства объектов в JavaScript являются перечисляемыми или не перечисляемыми, что в значительной степени напрямую связано с тем, отображается ли свойство в цикле for... in или нет. В браузерах, таких как Firefox и IE, как оказалось, arguments
числовые свойства объекта не являются перечисляемыми length
как бы), именно поэтому вы ничего не повторяете!
Но на самом деле, в конце концов, для итерации по всему, что является массивом или массивом, вам лучше использовать инкрементный цикл (как сказал М. Колодный) и вообще избегать этих махинаций (не говоря уже о потенциальном кросс-браузерном режиме). несоответствия - кажется, я заметил, что в Chrome 10 числовые свойства arguments
объекты перечислимы!)
Попробуйте использовать это как функцию форматирования:
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;
}
Теперь это должно работать:
alert('The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'))
Проверено и работает в IE
Из моего теста, с Firefox 3.6.13 и IE 8 я не вижу никакой разницы в поведении, они оба не идут во второй цикл.
Одно из различий между mycars и аргументами состоит в том, что mycars - это массив, а аргументы - это объект.
Чтобы продемонстрировать это:
alert(mycars.constructor); //shows: "function Array() { [native code] }"
alert(arguments.constructor); //shows: "function Object() { [native code] }"
Тем не менее, с некоторым тестовым кодом я вижу, что "for in" работает для Array и Object
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();
}
Итак, я не уверен, чем аргументы Object отличаются от Object, который вы создаете сами с помощью фигурных скобок. Я думаю, что это сбивает с толку, потому что в этом тесте работает как для Array, так и для Object. Хотя "for in" не работает с аргументами.
Опять же, во всех тестах я не заметил никакой разницы между FF 3.6 и IE 8.
ОБНОВЛЕНИЕ: Как я обнаружил благодаря комментариям Кена, свойства аргументов определены как не перечисляемые, в то время как при определении литерала свойства объекта неявно определяются как перечислимые.
Некоторые браузеры поддерживают for..in
например, Chrome и Firefox 4 для итерации аргументов, но другие браузеры не видят его параметры при такой итерации. Могу поспорить, что в этих браузерах, если вы сделаете JSON.stringify(arguments), результатом будет пустой объект. По спецификации JavaScript 1.1 и дальнейшие аргументы имеют параметр длины, так что это означает, что вы можете повторять их, используя for (var i = 0; i < arguments.length; i++)
а также while(i < arguments.length)
петли.
Лично однажды я сгорел с помощью for..in
для итерации аргумента я написал простую функцию для итерации объекта аргумента, которая не зависит от длины, потому что аргументы всегда помечаются по порядку идентификаторами.
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;
};
Я использую его с тех пор, когда перебираю аргументы, и это меня не подводит. Подробнее об этом можно узнать из моего блога http://stamat.wordpress.com/iterating-arguments-in-javascript/
Ну, это должно сработать, так что если это не так, ответ так же прост, как "это еще одна ошибка в IE".
Но реальный вопрос здесь заключается в том, почему вы используете for ... in
перебирать массивы или объекты, похожие на массивы (такие как arguments
) - просто используйте простой for
вместо этого цикл, который работает во всех основных браузерах:
for (var i = 0; i < arguments.length; i++) {
// do something with arguments[i]
}