Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Быстрый разбор списка элементов. (https://javascript.ru/forum/misc/10590-bystryjj-razbor-spiska-ehlementov.html)

B~Vladi 11.07.2010 12:23

Быстрый разбор списка элементов.
 
Всем мир.
На самом деле не надеюсь, что решение есть, но рискну спросить.
Как преобразовать список элементов в строку или массив?

Я пробовал так:
var arr = Array.prototype.slice.call(document.getElementsByTagName('*'));

и так:
var arr = Array.prototype.join.call(document.getElementsByTagName('*'));


и так:
function test(){
  var arr = Array.prototype.slice.call(arguments);
}
test.apply({}, document.getElementsByTagName('*'));


Осел такие трюки не понимает. Как его проучить?

И почему он arguments может преобразовать в массив, а список элементов и атрибутов нет?

зы: хотя я подозреваю почему... arguments - объект JScript, а остальное объекты DOM.

e1f 11.07.2010 14:46

Скорее всего, никак:
http://javascript.ru/forum/events/41...kh-tehgov.html

Octane 11.07.2010 14:57

Цитата:

Сообщение от B~Vladi
Я пробовал так:
var arr = Array.prototype.slice.call(document.getElementsByTagName('*'));

Цитата:

Сообщение от B~Vladi
Осел такие трюки не понимает.

IE9 теперь умеет так делать.

Цитата:

Сообщение от B~Vladi
Как его проучить?

// Преобразует NodeList в Array
// Реализация только для Standards Compliant Mode
if (!Array.toArray) {
	try {

		// IE9 и все остальные браузеры поддерживают преобразование NodeList в массив при помощи slice.
		Array.prototype.slice.call(document.childNodes); // IE6–8 здесь выбросят исключение.
		Array.toArray = function (slice) {
			return function (anything) {
				return slice.call(anything);
			};
		}(Array.prototype.slice);

	} catch (error) {

		Array.toArray = function (anything) {
			var length = anything.length, i = -1, array = [];
			if (length) {
				while (++i < length) {
					array[i] = anything[i];
				}
			}
			return array;
		};

	}
}

Riim 11.07.2010 16:04

Многие браузеры тупое переписывание выполняют быстрее чем slice.call . Так-что смысла нет столько всего писать, достаточно просто:
Array.toArray = function $A(iterable) {
	if (iterable) {
		var result = [], i = iterable.length;
		while (i) result[--i] = iterable[i];
		return result;
	}
	return [];
};

B~Vladi 11.07.2010 16:10

Octane, я так и думал, что без цикла не обойтись.
Раз уж так все плохо, то я бы для ишака максимально оптимизировал код:
Array.toArray = function (anything) {
  var l = anything.length;
  while (length--) {
    array[i] = anything[i];
  }
  return array;
};

Всем спасибо.

B~Vladi 11.07.2010 16:11

Цитата:

Сообщение от Riim
Многие браузеры тупое переписывание выполняют быстрее чем slice.call

Надо будет потестить.

Riim 11.07.2010 16:37

Цитата:

Сообщение от B~Vladi
while (length--) {

в последней итерации происходит лишнее перезаписывание переменной length. Не понимаю почему все так делают.

B~Vladi 11.07.2010 19:18

Цитата:

Сообщение от Riim
в последней итерации происходит лишнее перезаписывание переменной length

Есть вариант по-лучше?

e1f 11.07.2010 19:37

Цитата:

Сообщение от B~Vladi (Сообщение 63217)
Есть вариант по-лучше?

Array.toArray = function(anything) {
  var array = [];
  for (var l = anything.length, l--; ) {
    array[i] = anything[i];
  }
  return array;
};


UPD Хотя нет, туплю, тут тоже лишняя перезапись есть :)

рони 11.07.2010 20:29

просто как вариант...может так?
var r = document.getElementsByTagName("*");
Array.a = function (a) {
    var b = [];
    for (var c in a) if (typeof a[c] == "object") b[b.length] = a[c];
    return b
};
r = Array.a(r);

)))

x-yuri 11.07.2010 20:42

Цитата:

Сообщение от Riim
в последней итерации происходит лишнее перезаписывание переменной length. Не понимаю почему все так делают.

приоритеты другие. Хотя я тоже не понимаю, почему именно так делают

Цитата:

Сообщение от B~Vladi
Есть вариант по-лучше?

конечно, стандартные конструкции, что может быть лучше... :)
for( var i=0; i<anything.length; i++ )
    array.push( anything[i] );


Цитата:

Сообщение от рони
просто как вариант...может так?
var r=document.getElementsByTagName("*"); Array.a=function(a){var b=[];for(var c in a)if(typeof a[c]=="object")b[b.length]=a[c];return b}; r=Array.a(r);

экономим на пробелах? Не знаем о javascript компрессорах?

рони 11.07.2010 21:00

Вопрос: список элементов в массиве нужен или просто массив всего что есть в document.getElementsByTagName("*") ?
я про проверку на typeof == object
P.S. пробелы добавил ))) и да я много чего не знаю

Octane 11.07.2010 21:02

Цитата:

Сообщение от B~Vladi
я бы для ишака максимально оптимизировал код

Ага, я от push избавился, а порядок обхода забыл поменять.

B~Vladi 11.07.2010 23:15

Цитата:

Сообщение от x-yuri
var i=0; i<anything.length; i++

Этот цикл работает медленнее.
Цитата:

Сообщение от рони
я про проверку на typeof == object

Проверка не нужна.

B~Vladi 11.07.2010 23:18

Цитата:

Сообщение от x-yuri
Хотя я тоже не понимаю, почему именно так делают

Я делаю так, потому что:
1. в каждой итерации цикл выполняет одно действие
2. циклы в обратную сторону работают быстрее

x-yuri 12.07.2010 00:37

B~Vladi, а если бы не было javascript-компрессоров ты бы писал в одну строчку, используя однобуквенные переменные? ;) но вообще я сказал выше, что приоритеты имеют свойство отличаться

Riim 12.07.2010 00:44

Цитата:

Сообщение от B~Vladi
циклы в обратную сторону работают быстрее

я про то, что "--" делают в условии цикла, а не в его теле:
не понимаю почему while (i--) {a[i]}
вместо while (i) {a[--i]}
В первом варианте лишняя перезапись переменной в последней итерации, но ее оставляют как-будто намеренно.

рони 12.07.2010 01:32

Цитата:

Сообщение от x-yuri
а если бы не было javascript-компрессоров ты бы писал в одну строчку, используя однобуквенные переменные?

да))) обычно я так и пишу но благодаря форуму сейчас знаю что это стиль javascript-компрессоров
а по теме что чего быстрее https://blogs.oracle.com/greimer/ent..._way_to_code_a

x-yuri 12.07.2010 02:29

рони, я в общем-то B~Vladi отвечал и не то чтобы рассчитывал на ответ...

B~Vladi 12.07.2010 08:21

Riim,
Хм, точно, спасибо.

Kolyaj 12.07.2010 15:48

Цитата:

Сообщение от B~Vladi
Этот цикл работает медленнее.

Во сколько раз?

рони 12.07.2010 16:01

Цитата:

Сообщение от Kolyaj
Во сколько раз?

меня конечно не спрашивали но я приводил ссылку выше там можно посмотреть и сравнить время выполнения.
https://blogs.oracle.com/greimer/ent..._way_to_code_a

B~Vladi 13.07.2010 00:12

Ладно, с этим разобрались.
Чтобы не плодить тем, задам тут.

Использую такой код:
var frame = document.createElement('frame');
frame.href = 'about:blank'; // В принципе не обязательно, браузеры и так это грузят.
document.body.appendChild(frame);


После загрузки фрейма в WebKit, его документ находится в режиме совместимости... Собсно, как это исправить? Из-за этого много чего просто не работает.
Если грузить по урлу родительского окна, всё в порядке, но такой вариант не прокатит - грузяцо лишние байты, которые мне не нужны.

В общем, мне надо мне получить фрейм, чтобы динамически его наполнить элементами. А с этим режимом все валицо:(

Octane 13.07.2010 00:20

var iFrame = document.createElement("iframe");
document.body.appendChild(iFrame);
var iFrameDocument = iFrame.contentDocument;
iFrameDocument.open();
iFrameDocument.write('<!DOCTYPE html>');
iFrameDocument.close();
alert(iFrameDocument.compatMode);

B~Vladi 13.07.2010 08:06

:blink:
Старый добрый write... Совсем забыл про него.
Octane, спасибо.

Kolyaj 13.07.2010 09:39

Главное про document.close не забывай.

B~Vladi 13.07.2010 23:18

Вернусь к первому вопросу.

Столкнулся с одной интересной вещью. Есть код:
var fragment = document.createDocumentFragment();
for(var i = 0; i < 10000; i++){
  fragment.appendChild(document.createElement('div'));
}
document.body.appendChild(fragment);


Далее тестируем производительность в мозилле:
var arr = Array.prototype.slice.call(document.getElementsByTagName('div'));

var elems = document.getElementsByTagName('div');

console.time('While');
var l = elems.length;
var arrWhile = [];
while(l){
  arrWhile.push(elems[--l]);
}
console.timeEnd('While');


У меня показывает 9 ms. Если убрать первую строку или поменять 'div' на '*' (либо другой какой тег) в этой строке, начинает показывать 17 ms.
Это что? Кеширование?

e1f 14.07.2010 14:17

Что-то я не понял. Вот так видно, что кеширование коллекций какое-то есть, скорее всего (причем именно в FF, в Опере 10.60 все время тратится что-то на выборку, хотя сама она и происходит значительно быстрее):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Some test</title>

        <script type="text/javascript">
<!--
        function _init() {
            var fragment = document.createDocumentFragment();
            for (var i = 100000; i--; ) {
                fragment.appendChild(document.createElement('DIV'));
            }
            document.body.appendChild(fragment);
        }

        function _test() {
            var arr = [],
                results = document.getElementById('results'),
                start = (new Date()).getTime();

            // Get collection
            var elems = document.getElementsByTagName('DIV'),
                l = elems.length;

            results.appendChild(document.createTextNode('Find: ' +
                ((new Date()).getTime() - start) +
                'ms\n'));

            // Push
            start = (new Date()).getTime()
            while (l) {
                arr.push(elems[--l]);
            }

            results.appendChild(document.createTextNode('Push: ' +
                ((new Date()).getTime() - start) +
                'ms\n\n'));
        }
//-->
        </script>
    </head>

    <body onload="_init()"><button onclick="_test()">Test</button><pre id="results"></pre></body>
</html>

B~Vladi 14.07.2010 14:27

Цитата:

Сообщение от e1f
Что-то я не понял.

Дело не в том, что кеширование происходит при обращении к коллекции, а при преобразовании её в массив. На это я наткнулся, когда решил сравнить 2 способа преобразования на скорость. Когда я запустил последовательно 2 способа (см. приведённый выше скрипт) и посмотрел на консоль - решил, что while работает быстрее в 1.5 раза. Ну думаю, ок, будем юзать его. Убираю первый способ и мин 10 пытаюсь понять происходящее.
Вот как-то так всё получилось. Другие браузеры пока не тестил (не считая хрома - там все за 1 ms выполняется, не перестаю на него удивляцо).

e1f 14.07.2010 14:51

Занятно. Действительно, у меня первая пробежка с push по массиву на 50К занимает 160-170ms, со slice 150-160ms. Следующий запуск теста -- уже push 50-60, slice 40-50ms, то есть в принципе slice быстрее (не знаю, как Вы получили другой результат). НО! Подождите секунд 20-30, и поробуйте запустить тест снова -- опять большие цифры! То есть, кеширование происходит на весьма ограниченное время. Иллюстрация:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Some test</title>

        <script type="text/javascript">
<!--
        function _init() {
            var fragment = document.createDocumentFragment();
            for (var i = 50000; i--; ) {
                fragment.appendChild(document.createElement('DIV'));
            }
            document.body.appendChild(fragment);
        }

        function _test() {
            var arr = [],
                results = document.getElementById('results'),
                start = (new Date()).getTime();

            // Get collection
            var elems = document.getElementsByTagName('DIV'),
                l = elems.length;

            results.appendChild(document.createTextNode('Find: ' +
                ((new Date()).getTime() - start) +
                'ms\n'));

            if (Math.random() < 0.5) {
                _push();
                _slice();
            } else {
                _slice();
                _push();
            }

            results.appendChild(document.createTextNode('\n'));

            function _push() {
                arr = [];
                start = (new Date()).getTime();

                // Push
                while (l) {
                    arr.push(elems[--l]);
                }

                results.appendChild(document.createTextNode('Push: ' +
                    ((new Date()).getTime() - start) +
                    'ms\n'));
            }

            function _slice() {
                arr = [];
                start = (new Date()).getTime();

                // Slice
                arr = Array.prototype.slice.call(elems);

                results.appendChild(document.createTextNode('Slice: ' +
                    ((new Date()).getTime() - start) +
                    'ms\n'));
            }
        }
//-->
        </script>
    </head>

    <body onload="_init()"><pre id="results"></pre><button onclick="_test()">Test</button></body>
</html>

B~Vladi 14.07.2010 15:33

Выходит, действительно существует кеширование. Очень полезная инфа, я считаю.

e1f 14.07.2010 15:52

Да, интересный результат :)


Часовой пояс GMT +3, время: 21:28.