Недавно столкнулся с небольшой неприятностью, а именно: по разному отрабатывал код с for…in и обычным циклом for.
Передо мной стояла задача отрисовать график на canvas’е, данные которого хранились в JSON объекте. Для перебора всех свойств из объекта используется цикл по свойствам for..in. Так вот, цикл в разных вариациях вел себя по-разному. Чтобы разобраться в проблеме, я покажу как работает код в каждой вариации.
В качестве данных у нас будет вот такой небольшой объект, который представляет собой результат работы php-функции json_encode, которая была использована чтобы конвертировать в JSON выборку из БД. Реальная ситуация практически на любом проекте.
var data = {
0: {
x: 5,
y: 6
},
1: {
x: 10,
y: 4
},
3: {
x: 15,
y: 8
}
};
Данный код должен отрисовать на canvas’е порядковый номер каждого элемента объекта увеличенной на 1 (т.е вместо 0, 1, 2 — 1, 2, 3) оранжевым цветом, шрифт Arial высотой 10 точек, в координатах, заданных в самом объекте.
for..in
var canvas = document.getElementById('chart_canvas');
var context = canvas.getContext('2d');
for (var i in data) {
context.font = '10pt Arial';
context.fillStyle = '#ff9900';
context.fillText((i+1), data[i].x, data[i].y);
}
for
var canvas = document.getElementById('chart_canvas');
var context = canvas.getContext('2d');
for (var i = 0; i < data.length; i++) {
context.font = '10pt Arial';
context.fillStyle = '#ff9900';
context.fillText((i+1), data[i].x, data[i].y);
}
Результат
А что же выйдет на самом деле: первый вариант отобразит вместо ожидаемых 1, 2, 3 - 01, 11, 21. Не верите? Проверьте сами!
Почему? Да потому, что имя свойства обязано быть строкой. Если использовано значение другого типа — JavaScript приведет его к строке автоматически. Когда мы конвертировали массив в JSON, а после передали его в JavaScript, мы и представить не могли, что js посмеет не спросив нас выполнить преобразование типов, и наш int благополучно станет string.
for..in:
console.log(typeof i); // string
for:
console.log(typeof i); // number
Это в данном примере ошибка легко выявилась. Я же, должен признаться, поломал голову, почему у меня циклы по-разному отрабатывают. Тем более, что
console.log(i);
Выдавал одинаковые значения.
Чтобы все было как нам нужно, необходимо выполнить приведение типов:
for (var i in data) {
console.log(typeof i); // string
i = +i;
console.log(typeof i); // number
context.font = '10pt Arial';
context.fillStyle = '#ff9900';
context.fillText((i+1), data[i].x, data[i].y);
}
А вот теперь, все отработает как положено.
Будьте внимательны и не забывайте про типы, и про то, что имя свойства объекта является значение типа string.