jQuery.each 实现不同于原生 Array.forEach

Posted

技术标签:

【中文标题】jQuery.each 实现不同于原生 Array.forEach【英文标题】:jQuery.each implementation differs from native Array.forEach 【发布时间】:2012-10-17 05:33:24 【问题描述】:

有谁知道为什么原本出色的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
);​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

并不是它让我如此困扰,我更喜欢将原生 polyfills 用于最常见的新数组函数,但我一直对设计决策感到好奇。也许它是在浏览器实现本机 forEach 之前制作的,并且旧版支持阻止他们更改它,或者......?

或者,它是这样设计的,因为它也可以用于本机对象,然后在回调中将键放在值之前“有意义”...?

旁注:我知道 underscore.js(可能还有其他库)会反过来(更类似于原生函数)。

【问题讨论】:

jQuery 的不合格版本也无法像原生方法那样跳过数组中的漏洞。 附带说明,Underscore 不仅仅是“更类似于原生函数”;如果存在,它实际上使用本机函数(如果不存在,它使用专门设计用于镜像本机函数的实现)。 我知道这没关系,但我认为我什至不同意(entry, index) 的顺序。我不知道为什么不是(key, value),因为key 与数组的index 相同,您通常将其引用为key, value。只是我的观点... 对值进行操作的函数通常接受单个值参数。因此,本机迭代器将值作为第一个参数传递给回调非常方便。这使得函数更有用,因为它们可以很容易地独立使用或用作迭代器回调。例如,使用本机函数,我们可以将数组转换为每个成员的数值转换:arr.map(Number) 或者我们可以将其过滤为不允许数值转换的值列表:arr.filter(isNaN) 【参考方案1】:

好吧,我想我们必须向 Resig 先生本人寻求解释。事实上,ECMAscript 262 第 5 版 在设计和开发 jQuery 时并没有很普及,所以这肯定会发挥作用。而且由于它是这样设计的,他们不想在以后更改它并破坏所有现有代码。

事实上,在循环 Array 时,您更可能希望访问具有更高优先级的元素,而不是它的索引。所以,对我来说,没有合理的解释为什么要先将 index 传递给回调。

请放心,如果今天发明了 jQuery,它们将遵循本机实现行为。

另一方面,如果它给您带来太多困扰,您可以简单地创建一个快捷方式并使用本机 Array.prototype.forEach 来迭代您的 jQuery 包装集:

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

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

..对于标准 数组,只需使用其原生原型即可。

在执行条件返回假/真时,我们必须知道哪个部分以哪种方式工作。当您在 Array.prototype.forEach 中使用带有条件的 return false 时,它​​被视为继续,但是当您使用 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>

【讨论】:

【参考方案2】:

这实际上在上次 jQuery conf 中被提及;不幸的是,我不记得细节了,但是如果您观看了足够多的视频,我想您会找到它(我认为这是在主题演讲之后的问答期间,但不要引用我的话那)。无论如何,有人(我不认为是 Resig 本人,而是另一位 jQuery 组织的高级成员)基本上说“jQuery 有一些我们都知道有问题的东西(“每个”都是他们给出的例子之一)但是有数百万那里的网站现在依赖于这种有问题的行为,所以我们现在无法改变它。”

所以本质上,答案是他们做出了一个错误的决定(Resig 是一个令人难以置信的程序员,但即使他也不是完美的)现在每个使用 jQuery 的人都必须在剩下的时间里受苦,因为库的维护率相对较高新版本的向后兼容性(让我们面对现实吧,这是一件好事:您不希望每次升级 jQuery 时都必须重新编写整个网站)。

还值得一提的是,Underscore 库确实有一个each 方法,其签名与内置each 相同。事实上,Underscore 的版本实际上使用了内置版本(如果存在),为了获得更好的性能,如果浏览器不支持内置 each,则仅依赖其版本。由于 jQuery 对象只是数组,您可以轻松地将它们与 _.each 一起使用,并且由于 Underscore 具有 jQuery 所缺少的许多其他功能,因此它是补充 jQuery 的优秀库。

【讨论】:

P.S.如果你愿意,你可以随时使用 jQuery 的插件系统来“修复”这个问题。 P.P.S 在会议上,他们还提到未来版本(2.1 IIRC)将打破向后兼容性,所以我刚才所说的并不是 100% 正确的。但是,它只会在不再支持旧 IE 方面破坏向后兼容性;像每个这样的东西都不会得到修复。【参考方案3】:

我同意使用第二个 key 更有意义,但您必须记住 $.each 可以与对象或数组一起使用,对于对象,您可能需要键,因为它们有意义。

【讨论】:

以上是关于jQuery.each 实现不同于原生 Array.forEach的主要内容,如果未能解决你的问题,请参考以下文章

jQuery中的工具

原生JS forEach()和map()遍历,jQuery$.each()和$.map()遍历

jQuery核心语法

jQuery $.each用法[转]

jQuery $.each用法

jQuery $.each用法