Сравнение нативных javascripts для цикла loop с каждым () прототипа - PullRequest
4 голосов
/ 24 июля 2010

Итак, я спорил с коллегой-инженером о цикличности в JavaScript.Проблема касалась встроенной для конструкции цикла и метода each () прототипа.Теперь я знаю, что есть много документов / блогов о для и для каждого , но эта дискуссия несколько иная, и мне хотелось бы услышать, что думают некоторые из вас.

Давайте возьмем следующий цикл для примера

пример 1

var someArray = [blah, blah, blah,...,N];
var length = someArray.length;

for(var index = 0; index < length; index++){
    var value = someFunction(someArray[index]);
    var someOtherValue = someComplicatedFunction(value, someArray[index]);
    //do something interesting...       
}

Для меня это вторая натура, главным образом потому, что я научился кодировать в C , и это привело меня.Теперь я использую For-each в C # и Java (потерпите меня, я знаю, что мы говорим о JavaScript здесь ...), но всякий раз, когда яуслышать для петель , сначала я думаю об этом парне.Теперь давайте посмотрим на тот же пример, написанный с использованием метода Prototype: each ()

пример 2

var someArray = [blah, blah, blah,…..,N];
someArray.each(function(object){
    var value = someFunction(object);
    var someOtherValue = someComplicatedFunction(value, object);
    //do something interesting...   
});

В этом примере, сразу же, мы видим, чтоКонструкция содержит меньше кода, однако я думаю каждый раз, когда мы зацикливаемся на объекте, мы должны создать новую функцию для работы с рассматриваемой операцией.Таким образом, это плохо сформует коллекции с большим количеством объектов.Таким образом, аргумент моего приятеля состоял в том, что пример 2 гораздо проще для понимания и на самом деле чище, чем пример 1 из-за его функционального подхода.Мой аргумент заключается в том, что любой программист должен быть в состоянии понять пример 1, поскольку он преподается в программировании 101. Поэтому более простой аргумент не применяется, и пример 1 работает лучше, чем пример 2 .Тогда зачем возиться с # 2 .Теперь, после прочтения, я обнаружил, что когда размер массива мал, накладные расходы на пример 2 ничтожны.Однако люди продолжали говорить о строках кода, которые вы пишете, меньше и что пример 1 подвержен ошибкам.Я до сих пор не покупаю эти причины, поэтому я хотел знать, что вы, ребята, думаете ...

Ответы [ 5 ]

3 голосов
/ 24 июля 2010

Вы не создаете новую функцию на каждой итерации во втором примере. Есть только одна функция, которая продолжает вызываться снова и снова. Чтобы выяснить смысл использования только одной функции, подумайте, как бы вы сами реализовали метод each.

Array.prototype.each = function(fn) {
    for(var i = 0; i < this.length; i++) {
        // if "this" should refer to the current object, then
        // use call or apply on the fn object
        fn(this[i]);
    }
}

// prints each value (no new function is being created anywhere)
[1, 2, 3].each(function(v) { console.log(v); });

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

Лично я предпочитаю второй подход по той простой причине, что он освобождает меня от беспокойства о ненужных деталях, которые меня не должны волновать в первую очередь. Скажем, если мы перебираем коллекцию объектов employee, то index - это просто деталь реализации, и в большинстве случаев, если не все, ее можно абстрагировать от программиста с помощью таких конструкций, как метод each в Prototype, который путь теперь является стандартом в JavaScript, поскольку он был включен в ECMAScript 5-е издание под названием forEach.

var people = [ .. ];
for(var i /* 1 */ = 0; i /* 2 */ < people.length; i++ /* 3 */) {
    people[i /* 4 */].updateRecords();
    people[i /* 5 */].increaseSalary();
}

Отмечены все 5 вхождений, все i встроены в комментарии. Мы могли бы так легко полностью исключить индекс i.

people.forEach(function(person) {
    person.updateRecords();
    person.increaseSalary();
});

Для меня выгода не в меньших строках кода, а в удалении ненужных деталей из кода. Это та же самая причина, по которой большинство программистов запустили итератор в Java, когда он был доступен, а затем цикл 1011 * for, когда он появился. Аргумент, что те же основания не применимы к JavaScript, просто не применим.

2 голосов
/ 24 июля 2010

Пример первый не только подвержен ошибкам, для массивов тривиальной длины это плохой выбор - и каждый (или forEach, как определено в JavaScript 1.6, но IE8 все еще не поддерживает его) определенно по стилю лучший выбор.

Причина этого проста: вы говорите код , что делать, а не , как это сделать .В моих тестах с Firefox метод forEach примерно на 30% быстрее, чем цикл for.Но когда вы делаете крошечные массивы, это даже не имеет большого значения.Намного лучше сделать ваш код чище и проще для понимания (помните: что делает вместо того, чтобы как это сделать), чтобы не только ваше здравомыслие при следующем возвращении к нему, но для здравомыслия любого, кто смотрит на ваш код.

Если единственная причина, по которой вы включаете прототип, - это метод .each, вы делаете это неправильно.Если вам нужен только метод чистой итерации, используйте метод .forEach, но не забудьте определить свой собственный.Это со страницы MDC на forEach - полезная проверка, чтобы дать себе метод .forEach, если его не существует:

if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp*/)
  {
    var len = this.length >>> 0;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        fun.call(thisp, this[i], i, this);
    }
  };
}

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

2 голосов
/ 24 июля 2010

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

Но всякий раз, когда вы спрашиваете, что быстрее, вы должны спросить, действительно ли это действительно имеет значение?Вы поместили на него профилировщик кода и проверили его на реальных данных?Вы можете потратить много времени на выяснение того, что быстрее, но если это составляет только 0,0001% вашего времени выполнения, кого это волнует?Используйте инструменты профилирования, чтобы найти узкие места, которые действительно имеют значение, и используйте любой метод итерации, который, как вы и ваша команда согласитесь, легче использовать и читать.

0 голосов
/ 24 июля 2010

IMO, второй подход лаконичен и проще в использовании, хотя и усложняется (см. Документ-прототип), если вы хотите использовать такие вещи, как break или continue. См. каждая документация.

Так что, если вы используете простую итерацию, использование функции each () лучше IMO, так как она может быть более краткой и простой для понимания, хотя и менее производительной, чем необработанный цикл for

0 голосов
/ 24 июля 2010

Ответ - его субъективно.

Для меня пример 2 на самом деле не намного меньше кода, а также включает в себя загрузку (пропускную способность) И анализ (/ время выполнения) библиотеки ~ 30 КБ передВы даже можете использовать его - так что сам метод не только сам по себе менее эффективен, но и требует дополнительных настроек.Для меня - утверждать, что пример 2 лучше - это безумие - однако это всего лишь мнение, что многие будут (и имеют на это полное право) не согласиться полностью.

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