1 (изменено: Rumata, 2012-02-20 02:34:15)

Тема: Javascript/JScript: правильное использование циклов for-in и for

Несколько вводных слов. В Javascript/JScript есть два цикла, предназначенные для перебора элементов. Каждый из них предназначен для своих определенных задач. Предлагаю выделить эту тему, чтобы она всегда находилась наверху списка, так как она напрямую отражает особенности языка

for-in: перебор свойств объекта


//Object.prototype.foo = 'bar';
var obj = {
	a: 1, 
	b: 2
};

for (var p in obj) {
	alert(p + '=' + obj[p]);
}

Этот цикл перебирает все свойства объекта, проходя по всей цепочке прототипов. Это значит, если некоторый объект унаследовал некоторое свойство из прототипа, то оно тоже будет включено в цикл перебора. Для проверки запустите пример в браузере, а потом уберите комментарий в первой строке и запустите еще раз. Чтобы отфильтровать нежелательные свойства рекомендуется использовать метод .hasOwnProperty. Особенность метода заключается в том, что он проверяет существование свойства у самого объекта без заглядывания в цепочку прототипов:


Object.prototype.foo = 'bar';
var obj = {
	a: 1, 
	b: 2
};

for (var p in obj) {
	if ( obj.hasOwnProperty(p) ) {
		alert(p + '=' + obj[p]);
	}
}

Это лучший, но не единственный способ использования цикла. В некоторых фреймфорках можно встретить конструкцию, сходную с этой:


Object.prototype.foo = 'bar';
var obj = {
	a: 1, 
	b: 2
};

var empty = {};

for (var p in obj) {
	if ( p in empty ) {
		continue;
	}
	alert(p + '=' + obj[p]);
}

Суть этого способа - в отбрасывании всех общих, унаследованных свойств реального объекта и фиктивного emtpy. Недостатком, на мой взгляд, является необходимость в создании фиктивной переменной.

for: перебор элементов массива

Javascript (и его диалект JScript) своеобразный язык - в нем фактически нет типа данных массив. По сути, массив (в обычном понимании этого термина) -- специальный объект со специальным свойством .length - максимальным значением индекса следующего вставляемого элемента. Именно поэтому массивы не рекомендуется использовать с циклом for-in, потому что последний перебирает все свойства массива, как объекта, что может оказаться нежелательно.

Реальный пример.
Javascript от Mozilla реализует несколько полезных и стандартных методов, которые весьма удобны при работе с массивами, и которые не реализованы в ранних версиях JScript от Microsoft, например .indexOf, .lastIndexOf, .forEach и т.д. Поэтому многие разработчики дописывают в своих фреймворках эти методы самостоятельно, добавляя их в прототип:


if ( ! Array.prototype.indexOf ) {

Arrat.prototype.indexOf = function(element)
{
	for (var i = 0; i < this.length; i++) {
		if ( element === this[i] ) {
			return i;
		}
	}
	return -1;
};

}

var array = [1, 2, 3];
for (var p in array) {
	alert(array[p]);
}

В таком случае в цикле будут также перечислены и добавленные методы, что крайне нежелательно. Поэтому классический цикл for - самый правильный применительно к массивам:


var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
	alert(array[i]);
}

По этой же причине (переопределение прототипа объекта - в данном случае массива) рекомендуется использовать метод .hasOwnProperty с объектами - чтобы исключить из перебора все "лишние" свойства объекта.

Так как массивы теоретически могут быть разреженными, то самым гарантированно правильным перебором элементов считается следующий:


var array = [1,, , 4, , 6];

for (var i = 0; i < array.length; i++) {
	if ( i in array ) {
		alert(array[i]);
	}
}

Ссылки

Перевод спецификации на сайте javascript.ru
Стандарт языка: for-in
Стандарт языка: for

Документация на сайте mozilla
mozilla: for-in
mozilla: for

Документация на сайте microsoft
microsoft: for-in
microsoft: for

Сады Javascript: разделы "Объекты" и "Массивы"

Примечания

Техническое описание стандарта языка может показаться скучным и неинтересным, но без этого список ссылок описание было бы неполным. Страницы Microsoft крайне скупы на информацию, поэтому рекомендуется справляться с описанием на сайте Mozilla. Крайне рекомендуется к прочтению документация на сайте Сады Javascript, так как он представляет собой документацию по самым заковыристым темам языка JavaScript.

( 2 * b ) || ! ( 2 * b )