При просмотре dom.js источника из библиотеки Closure я нашел это (в goog.dom.getElementsByTagNameAndClass_
):
if (opt_class) {
var arrayLike = {};
var len = 0;
for (var i = 0, el; el = els[i]; i++) {
var className = el.className;
// Check if className has a split function since SVG className does not.
if (typeof className.split == 'function' &&
goog.array.contains(className.split(' '), opt_class)) {
arrayLike[len++] = el;
}
}
arrayLike.length = len;
return arrayLike;
}
Каково было бы преимущество выполнения этого по эквидистантной антенной решетке?
Автор кода использовал пустой объект JavaScript в качестве основы подобного массиву объекта, т.е. такого, к которому можно получить доступ по индексу и который имеет свойство длины.
Могут быть две причины, о которых я мог бы подумать: Использование памяти - если массив поддерживается реализацией, которая выделяет n элементов, то при достижении предела его емкости он увеличивается в какой-то степени и, таким образом, тратит емкость - длину
памяти
Готов поспорить, что подобный код будет найден и в других JavaScript библиотеках, и что это результат бенчмаркинга и поиска наиболее подходящего решения в разных браузерах.
отредактированный после комментария Джастина
После дальнейшего гугления кажется, что массивоподобные объекты широко распространены среди разработчиков JavaScript: если посмотреть на JavaScript: окончательное руководство Дэвида Флэнагана, в нем есть целый подраздел, посвященный массивоподобным объектам. Также эти ребята упоминают их.
Никаких упоминаний о том, почему следует отдавать предпочтение массивоподобным объектам по сравнению с массивами. Это может быть хорошим SO вопросом.
Таким образом, третьим вариантом может быть ключ: соответствие нормам JavaScript API.
.goog.dom.getElementsByTagNameAndClass_
Вы имеете дело с коллекциями html. Объект arrayLike - это снэпшот списка узлов, а не объект "живой" коллекции. Он позволяет работать с ним так же просто, как с индексированным массивом, и с меньшей вероятностью вызывает осложнения, если вы создаете или удаляете узлы, проходя через его члены.
В данном случае, на мой взгляд, arrayLike[len++] = el
является оптимизацией по сравнению с actualArray.push(el)
. Однако после выполнения простого бенчмарка (код, приведенный ниже) оказывается, что этот метод на самом деле медленнее, чем использование стандартного массива с помощью метода push
, а также с помощью той же самой техники построения.
Результаты (из OS X 10.5.8, FF 3.5.6) *:
push construction: 199ms (fastest)
indexed construction: 209ms
associative construction: 258ms (slowest)
В заключение, почему Closure в данном случае использует ассоциативный массив, выходит за рамки меня. Скорее всего, на то есть причина (например, эта техника может лучше работать в Chrome, или, что менее сомнительно, эта техника может лучше работать в будущих релизах JavaScript-движков в целом), но я не вижу веской причины в этом случае.
* Среднее значение не было приведено, так как время варьировалось от тестового прогона к тестовому, но последовательно приводило к одному и тому же порядку. Если вам интересно, то вы можете сделать это самостоятельно.
* Код бенчмарка:
var MAX = 100000, i = 0,
a1 = {}, a2 = [], a3 = [],
value = "";
for ( i=0; i<1024; ++i ) {
value += "a";
}
console.time("associative construction");
for ( i=0; i<MAX; ++i ) {
a1[i] = value;
}
a1.length = i;
console.timeEnd("associative construction");
console.time("push construction");
for ( i=0; i<MAX; ++i ) {
a2.push(value);
}
console.timeEnd("push construction");
console.time("indexed construction");
for ( i=0; i<MAX; ++i ) {
a3[i] = value;
}
console.timeEnd("indexed construction");
* Размер и тип значения *
несущественны для теста, так как JavaScript использует копирование на запись. Большое (1 кб) значение было использовано для того, чтобы убедить тех читателей, которые не знакомы с этой особенностью JavaScript.
Насколько я знаю, в этом нет никакой пользы, потому что на самом деле нет никакой разницы между этим и "обычным массивом", кроме синтаксиса создания - все объекты Javascript являются ассоциативными массивами.
. не вижу разницы, так как alert(typeof []);
возвращает "объект". Всякий раз, когда я вижу что-то подобное (особенно от Goog), я должен предполагать, что это для повышения производительности.
По сути, это возврат массива без всех унаследованных функций, таких как push
и pop
.
Я думаю, что в этом примере вместо реального массива создается массивоподобный объект, потому что другие методы DOM также возвращают массивоподобные объекты (NodeList).
Последовательное использование "массивов-объектов" в API заставляет разработчика избегать использования методов, специфичных для массивов (используя goog.array
вместо этого), так что будет меньше попаданий, когда кто-то позже решит изменить вызов getElementsByTagNameAndClass на, скажем, getElementByTagName.