Реализация jQuery.each отличается от родной Array.forEach

Кто-нибудь знает, почему отличная в остальном функция jQuery.each разработана иначе, чем (теперь) родная Array.forEach? Например:

var arr = ['abc','def'];
arr.forEach(function(entry, index) {
    console.log(entry); // abc / def
});

Это имеет абсолютный смысл. Но jQuery решил поставить index в качестве первого аргумента:

$.each(arr, function(index, entry) {
   console.log(entry);
});

Кто-нибудь знает причину этого дизайнерского решения? Я всегда широко использовал $.each, но меня всегда раздражало, что индекс был первым аргументом, так как он редко используется. Я знаю, что в jQuery реализована прямая ссылка через this, но это очень запутывает, если вы это сделаете:

​var arr = ['abc','def'];
$.each(arr, function() {
    console.log(this === 'abc'); // false both times, since this is a String constructor
});​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

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

Или, может быть, он разработан таким образом, потому что его можно использовать и для нативных объектов, тогда «имеет смысл» помещать ключ перед значением в обратном вызове...?

Примечание: я знаю, что underscore.js (и, возможно, другие библиотеки) делает это наоборот (больше похоже на нативную функцию).


person David Hellsing    schedule 26.10.2012    source источник
comment
Несоответствующая версия jQuery также не может пропускать дыры в массиве, как это делают нативные методы.   -  person I Hate Lazy    schedule 27.10.2012
comment
В качестве примечания: Underscore не просто больше похож на нативную функцию; он фактически использует нативную функцию, если она присутствует (а если нет, то использует свою реализацию, которая специально разработана для отражения нативной).   -  person machineghost    schedule 27.10.2012
comment
Я знаю, что это не имеет значения, но я не думаю, что даже согласен с тем, что (entry, index) находится в том порядке, в котором они есть. Я не знаю, почему это не было бы (key, value), поскольку key совпадает с index для массивов, и вы обычно ссылаетесь на вещи как key, value. Просто моя точка зрения...   -  person Ian    schedule 27.10.2012
comment
Функции, работающие со значением, часто принимают аргумент с одним значением. Из-за этого очень удобно, что собственные итераторы передают значение в качестве первого аргумента обратного вызова. Это делает функции более полезными, поскольку их можно легко использовать автономно или в качестве обратного вызова итератора. Например, используя собственные функции, мы можем преобразовать массив в числовое преобразование каждого члена: arr.map(Number) Или мы можем отфильтровать его до списка значений, которые не допускают числовое преобразование: arr.filter(isNaN).   -  person I Hate Lazy    schedule 27.10.2012


Ответы (3)


Что ж, я думаю, нам придется попросить самого мистера Резига объяснить это. Дело в том, что ECMAscript 262 edition 5 не был очень широко распространен во время проектирования и разработки jQuery, так что это определенно играет роль. И так как он был разработан таким образом, они не хотели менять его позже и ломать весь существующий код.

На самом деле гораздо более вероятно, что вы захотите получить доступ к элементу с более высоким приоритетом, чем к его индексу, когда зацикливаете массив. Итак, для меня нет разумного объяснения, почему вы должны сначала передать index в обратные вызовы.

Будьте уверены, если бы jQuery был изобретен сегодня, они бы следовали поведению нативной реализации.

С другой стороны, если это вас слишком сильно беспокоит, вы можете просто создать ярлык и использовать собственный Array.prototype.forEach для итерации ваших обернутых наборов jQuery:

var forEach = Function.prototype.call.bind( Array.prototype.forEach );

forEach( $('div'), function( node ) {
    console.log( node );
});

.. а для стандартных массивов используйте их собственный прототип.

в то время как реализация условного возврата false/true, мы должны знать, какая часть работает каким образом. Когда вы используете return false с условием в Array.prototype.forEach, он обрабатывается как продолжение, но когда вы используете return false с условием в $.each, он обрабатывается как оператор break.

var listArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
var arr1 =[];var arr2=[];
var rv = true;
listArray.forEach(function(i, item) {
  if (i == 5) {
    return rv = false;
  }
 arr1.push(i)
  return rv;
});
var listArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
jQuery.each(listArray, function(i, item) {
  if (item == 5) {
    return rv = false;
  }
  arr2.push(i)
});
  console.log("forEach=>"+arr1)
  console.log("$.each=>"+arr2)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

person jAndy    schedule 26.10.2012

На самом деле это было упомянуто на последней конференции jQuery; к сожалению, я не помню подробностей, но если вы посмотрите достаточно видео с него, я думаю, вы сможете его найти (я думаю, это было во время вопросов и ответов после основного доклада, но не цитируйте меня на тот). Как бы то ни было, кто-то (я не думаю, что это был сам Ресиг, а другой старший член организации jQuery) в основном сказал: «В jQuery есть некоторые вещи, которые, как мы все знаем, проблематичны («каждый» является одним из примеров, которые они привели), но есть миллионы сайты, которые теперь полагаются на это проблемное поведение, поэтому мы не можем изменить его сейчас».

Так что, по сути, ответ в том, что они приняли неверное решение (Ресиг — НЕВЕРОЯТНЫЙ программист, но даже он не идеален), и теперь всем, кто использует jQuery, приходится страдать до конца своих дней, потому что библиотека поддерживает относительно высокую обратную совместимость в новые версии (и, скажем прямо, это хорошо: вам не нужно переписывать весь сайт каждый раз, когда вы обновляете jQuery).

Также стоит упомянуть, что в библиотеке Underscore действительно есть метод each с той же сигнатурой, что и у встроенного each. На самом деле версия Underscore фактически использует встроенную версию, если она имеется, для повышения производительности, полагаясь только на свою версию, если браузер не поддерживает встроенный each. Поскольку объекты jQuery — это просто массивы, вы можете легко использовать их с _.each, а поскольку Underscore имеет много других функций, отсутствующих в jQuery, это отличная библиотека для дополнения jQuery.

person machineghost    schedule 26.10.2012
comment
P.S. Вы всегда можете использовать систему подключаемых модулей jQuery, чтобы решить эту проблему, если хотите. - person machineghost; 27.10.2012
comment
PPS На конференции они также упомянули, что будущая версия (2.1 IIRC) нарушит обратную совместимость, поэтому то, что я только что сказал, не является правдой на 100%. Тем не менее, это только нарушит обратную совместимость с точки зрения прекращения поддержки старых IE; такие вещи, как каждый, не собираются исправляться. - person machineghost; 27.10.2012

Что ж, я согласен, что имеет смысл иметь key секунду, но вы должны иметь в виду, что $.each можно использовать с объектом или массивом, и в случае объекта вам могут понадобиться ключи, поскольку они имеют значение.

person prodigitalson    schedule 26.10.2012