Является ли передача нескольких наборов аргументов в виде массива в JavaScript быстрее, чем несколько вызовов функций? - PullRequest
0 голосов
/ 12 сентября 2009

После того, как мне стало немного не по себе от нескольких вызовов одной и той же функции с разными параметрами (как показано в разделе кода dom ready) ниже, я решил проверить передачу параметров для этой функции, вместо этого перебирая массив ( как показано в mouse_function_two). К моему удивлению, я обнаружил, что mouse_function_two работает значительно быстрее.

У меня такой вопрос, будет ли mouse_function_two считаться лучшей практикой JavaScript, чем mouse_function_one?

Примечание: я немного обеспокоен тем, что могу неправильно использовать утилиту firebugs console.time!

Начальная попытка

function mouse_function_one (hover_selector, class_name, add_link) {

  hover_item = $(hover_selector)

  hover_item.each(function(){
    $(this).hover(
      function () {
        $(this).addClass(class_name);
        if ( add_link == true ) {
          $(this).click(function(){
            var href = $(this).find('a').attr('href');
            window.location.href = href;
          });

        }
        else return false;
      },
      function () {
        $(this).removeClass(class_name);
    });
  });
}

Вторая (более быстрая) попытка:

function mouse_function_two (args) {

  for (var i=0; i < args.length; i++) {

    hover_selector = $(args[i][0]);
    class_name = args[i][1];
    add_link = args[i][2];

    hover_item = $(hover_selector)

    hover_selector.each(function(){
      $(this).hover(
        function () {
          $(this).addClass(class_name);
          if ( add_link == true ) {
            $(this).click(function(){
              var href = $(this).find('a').attr('href');
              window.location.href = href;
            });

          }
          else return false;
        },
        function () {
          $(this).removeClass(class_name);
        });
    });

  }
}

Код клиента:

// on dom ready
$(function(){

  console.time('f1');
  mouse_function_one('#newsletter .right', 'hovered', true);
  mouse_function_one('.block-couple div.right', 'hovered', false);
  mouse_function_one('.bulletin', 'selected', true);
  console.timeEnd('f1'); //speed is calculated as 104ms

  args = [];
  args[0] = ['#newsletter .right', 'hovered', true]; 
  args[1] = ['.block-couple div.right', 'hovered', false]; 
  args[2] = ['.bulletin', 'selected', true]; 

  console.time('f2');
  mouse_function_two(args);
  console.timeEnd('f2'); //speed is calculated as 10ms

});

Ответы [ 3 ]

4 голосов
/ 12 сентября 2009

Если вторая подпрограмма быстрее, вероятно, потому, что она не выполняет то, что должна. Посмотрите на этот фрагмент:

  for (var i=0; i < args.length; i++) {

    hover_selector = $(args[i][0]);
    class_name = args[i][1];
    add_link = args[i][2];

    hover_item = $(hover_selector)

    hover_selector.each(function(){
      $(this).hover(
        function () {

Две проблемы здесь:

  1. Вы используете неявные глобальные переменные.
  2. Блоки в JavaScript не вводят новую область видимости.

Любой из них вызовет одну и ту же ошибку, вместе они просто вдвойне гарантируют, что это произойдет: замыкания, созданные для функций обработчика событий hover(), содержат только значения последней итерации цикла. Когда эти обработчики будут наконец вызваны, class_name всегда будет "selected", а add_link всегда будет true. Напротив, ваша исходная функция будет вызываться каждый раз с другим набором параметров, которые будут захвачены обработчиками событий в области действия функции и, следовательно, работать как положено.


Что касается стиля ... Это грязно. Вы заключили все тело функции в цикл, удалили описательные аргументы и значительно усложнили вызов функции.

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

// For starters, I've eliminated explicit parameters completely.
// args wasn't descriptive, and all JavaScript functions have an implicit
// arguments array already - so let's just use that.
function mouse_function_three () {

  // use jQuery's array iteration routine:
  // this takes a function as an argument, thereby introducing scope and
  // avoiding the problem identified previously
  $.each(arguments, function() {
    var class_name = this.class_name;
    var add_link = this.add_link;
    var selected = $(this.hover_selector);

    // no need to use selected.each() - jQuery event binding routines
    // always bind to all selected elements
    selected.hover(function() {
      $(this).addClass(class_name);
    },
    function() {
      $(this).removeClass(class_name);
    });

    // bring this out of the hover handler to avoid re-binding
    if ( add_link ) {
      $(selected).click(function(){
        var href = $(this).find('a').attr('href');
        window.location.href = href;
      });

    }
  }); // each set of arguments
}

Затем вы бы назвали эту новую процедуру так:

console.time('f3');
mouse_function_three( 
 {hover_selector: '#newsletter .right', class_name: 'hovered', add_link: true},
 {hover_selector: '.block-couple div.right', class_name: 'hovered', add_link: false},
 {hover_selector: '.bulletin', class_name: 'selected', add_link: true}
);
console.timeEnd('f3');

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

3 голосов
/ 12 сентября 2009

Это кусок кода узкого места? Использование массивов в качестве аргументов открывает вам целую категорию ошибок, от которых вас защищают именованные параметры. 2-й код читается довольно плохо, и, если это не так, со временем окажется полной задницей для отладки. Так что, если это не критично, сохраняйте это в здравом уме. Только когда это действительно проблемный код, вы должны начать отбрасывать разумные языковые возможности, чтобы немного ускорить процесс.

РЕДАКТИРОВАТЬ: Чем больше я смотрю на код, тем менее сбалансированным кажется ваш тест. Есть ряд вещей, которые вы не учли, что может привести к другому выводу, например, стоимость повторного вызова метода в течение одного вызова и что заполнение массивов и т. Д. Не проверяется во втором случае.

0 голосов
/ 12 сентября 2009

Я бы рассмотрел передачу объекта JSON в вашем случае. Фреймворки, такие как ExtJs и JQuery, в значительной степени полагаются на них, поскольку делая это, вы получаете большую гибкость (особенно по мере развития вашего API) и позволяете упаковывать много данных в один объект.

function mouse_function_three (args) { ... }

где args выглядит как ...

[{'hover_selector': ... , 'class_name': ... , 'add_link': ...}, {'hover_selector': ... , 'class_name': ... , 'add_link': ...}, ... ]

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

{
'default_hover_selector': 'asdf',
'default_class': 'asdf',
'add_link: false,
'items': [{'hover_selector': ... , 'class_name': ... , 'add_link': ...}, {'hover_selector': ... , 'class_name': ... , 'add_link': ...}, ... ]
}

Я думаю, что это решение даст вам производительность и интуитивность (это даже слово), которую вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...