Почему этот Javascript намного * медленнее *, чем его эквивалент jQuery?

#javascript #jquery #performance

#javascript #jquery #Производительность

Вопрос:

У меня есть HTML-список из примерно 500 элементов и поле «фильтр» над ним. Я начал с использования jQuery для фильтрации списка при вводе буквы (код синхронизации добавлен позже):

 $('#filter').keyup( function() {
    var jqStart = (new Date).getTime();

    var search = $(this).val().toLowerCase();
    var $list = $('ul.ablist > li');

    $list.each( function() {
        if ( $(this).text().toLowerCase().indexOf(search) === -1 )
            $(this).hide();
        else
            $(this).show();
    } );

    console.log('Time: '   ((new Date).getTime() - jqStart));
} );
 

Однако после ввода каждой буквы (особенно первой буквы) была задержка на пару секунд. Поэтому я подумал, что это может быть немного быстрее, если я использую обычный Javascript (недавно я прочитал, что функция jQuery each работает особенно медленно). Вот мой эквивалент JS:

 document.getElementById('filter').addEventListener( 'keyup', function () {
    var jsStart = (new Date).getTime();

    var search = this.value.toLowerCase();
    var list = document.querySelectorAll('ul.ablist > li');
    for ( var i = 0; i < list.length; i   )
    {
        if ( list[i].innerText.toLowerCase().indexOf(search) === -1 )
            list[i].style.display = 'none';
        else
            list[i].style.display = 'block';
    }

    console.log('Time: '   ((new Date).getTime() - jsStart));
}, false );
 

Однако, к моему удивлению, простой Javascript работает в 10 раз медленнее, чем эквивалент jQuery. Для фильтрации каждой буквы в версии jQuery требуется около 2-3 секунд, в то время как версия Javascript занимает более 17 секунд! Я использую Google Chrome в Ubuntu Linux.

Это не для чего-то действительно важного, поэтому оно не должно быть суперэффективным. Но я делаю что-то действительно глупое с моим Javascript здесь?

Комментарии:

1. Я бы засек время двух селекторов и посмотрел, все ли там; Я знаю, что материал jQuery selector настроен до смерти.

2. Кэширование list.length — это одно, хотя это не имело бы такого большого значения. Также считается, что ваш метод синхронизации неточен. ejohn.org/blog/accuracy-of-javascript-time

3. @Dave: jQuery стремится использовать как можно больше встроенной поддержки селекторов браузера, в основном любой селектор, который не зависит от jQuery, должен обрабатываться собственным интерфейсом браузера, потому что это будет значительно быстрее, чем делать это в JavaScript.

4. @DaveNewton querySelectorAll = 1 мс, $() = 3 мс. Определенно не селекторы!

5. Попробуйте заменить textContent на innerText , чтобы увидеть, имеет ли это значение. Кроме того, есть ли причина, по которой вы запускаете 'ul.ablist > li' событие on each keyup? Ожидается ли, что он изменится? Если это так, я бы использовал живой список узлов. В любом случае, я бы кэшировал его вне обработчика.

Ответ №1:

Вы могли бы попробовать использовать textContent вместо innerText , я думаю, это должно быть быстрее. Кроме того, синхронизация генерации списка и цикла отдельно покажет, есть ли проблема в генерации списка.

Комментарии:

1. Спасибо, использование textContent полностью решило проблему — фильтрация теперь мгновенная! 17 000 мс до ~ 8 мс: D

2. 1 = 10. Это бронзовый значок 😉 @disappuntledgoat Дополнительная информация: внешние запятые в ((new Date).getTime - jsStart) не являются строго необходимыми, потому что ваше выражение JavaScript вычисляется справа налево. Смотрите также: developer.mozilla.org/en/JavaScript/Reference/Operators /…

3. @RobW получил значок. Приветствия !

Ответ №2:

Еще одна лучшая практика для повышения скорости javascript — это кэширование list.length в переменной и вызов переменной, например:

 l = list.length;
for (var i=0;i<l;i  ):{ code here}
 

И, возможно, синхронизация с jsperf была бы лучше.

Ответ №3:

Здесь я немного переработал ваш код:

 var filter = document.getElementById( 'filter' ),
    ablist = document.querySelector( '.ablist' );

filter.addEventListener( 'keyup', function () {
    var re, elems, i, len, elem;

    re = RegExp( this.value, 'i' );
    elems = ablist.children;

    for ( i = 0, len = elems.length; i < len; i  = 1 ) {
        elem = elems[i];       
        elem.style.display = 
                elem.textContent.search( re ) > -1 ? 'list-item' : 'none';
    }
}, false );
 

Живая демонстрация: http: //jsfiddle.net/MVFxn/

Изменения:

  • с регулярным выражением и i флагом нет необходимости toLowerCase ,
  • если на странице есть только один '.ablist' элемент, querySelector это должен быть самый быстрый способ его захвата (поскольку он прерывает запрос, как только находит первый такой элемент),
  • для элементов LI нет запроса, поскольку children свойство уже удобно ссылается на них.

Я хотел бы знать, как этот код работает на вашей странице…

Ответ №4:

Я использовал while вместо for и внес некоторые незначительные улучшения. Вот окончательный код.

 var list = list = document.querySelectorAll('ul.ablist > li');
document.getElementById('javascriptFilter').addEventListener( 'keyup', function () {
    var jsStart = (new Date).getTime(),
        search = this.value.toLowerCase(),
        i = list.length - 1,
        listItem,
        resu<
    while( i >= 0 )
    {
        listItem = list[i];
        if ( listItem.textContent.toLowerCase().indexOf(search) === -1 )
            listItem.style.display = 'none';
        else
            listItem.style.display = 'block';
        i--;
    }

    result = ((new Date).getTime() - jsStart);
    console.log(['Time: ', result, '<br />'].join(''));
}, false );