【问题标题】:Why does iterating over jQuery objects with .each() not give me jQuery objects?为什么用 .each() 遍历 jQuery 对象不会给我 jQuery 对象?
【发布时间】:2025-12-12 11:15:01
【问题描述】:

以下按预期工作:

$(".foo").first().text("hi!")

...因为 first() 返回一个 jQuery 对象。

但是,如果我想对所有匹配项使用 text() 方法,我需要这样做:

$(".foo").each( function(idx, obj) {
  $(obj).text("hi!")
  }
)

...因为each() 为您提供 DOM 对象。

这种令人费解的差异背后的设计原因是什么?如何避免为每个匹配构建一个 jQuery 对象?

【问题讨论】:

    标签: jquery


    【解决方案1】:

    可能是由于与循环大型集合相关的性能原因?如果您只需要 DOM 对象,那么您可以节省周期。如果您需要 jQuery 对象,那么您可以轻松获得。

    我通常不为每个参数提供第二个参数,所以我可以使用 $(this)。

    【讨论】:

    • 正是我要说的,加上如果 first() 没有返回 jQuery 对象,你必须使用 $($(selector).first()),这有点冲突jQuery 的目标是简洁。
    • 好吧,我的印象是$(".foo") 使 jQuery 对象开始,并且在通过each() 时被剥离为“基本”DOM 对象。我想这不是这里发生的事情:)
    • @Jon - 这是不正确的。 $(selector).first() 确实 返回一个 jQuery 对象。你不应该用$($(selector).first()) 再次包装它。唯一返回纯 DOM 元素的 jQuery 方法(据我所知)是 .get( 0 ),当您向它传递一个数字时。如果没有 number 参数,您将获得一个 DOM 元素数组。
    • @patrick dw:我是在指出如果 first() 没有返回 jQuery 对象你必须做什么,说明为什么 first() 返回 jQuery 对象是有意义的。
    【解决方案2】:

    在 jQuery 内部调用 $("sel").each(function(){});

    if ( isObj ) {
        for ( name in object ) {
            if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                break;
            }
        }
    }
    

    eq 是一个简单的切片:

    eq: function( i ) {
        return i === -1 ?
        this.slice( i ) :
        this.slice( i, +i + 1 );
    }
    

    因此您可以创建一个新的 each 函数,而不是 object[name] 将执行 object:eq(i)

    $("*").slice(1,2).toSource() == $("*").eq(1).toSource();
    

    所以要创建你自己的each

    $.fn.each2 = function(callback)
    {
       for ( var i = 0; i < this.length; ++i ) {
          callback.call( this.eq(i), i, this.eq(i) )
       }
    };
    
    $("*").each2(function(i, obj) {
        alert(obj); // now obj is a jQuery object
    });
    

    看来each3each2http://www.jsfiddle.net/m7pKk/2/

    $.fn.each2 = function(callback)
    {
       for ( var i = 0; i < this.length; ++i ) {
           var jObj = this.eq(i);
          callback.call( jObj, i, jObj )
       }
    };
    
    $.fn.each3 = function(callback)
    {
       for ( var i = 0; i < this.length; ++i ) {
           var jObj = $(this[i]);
          callback.call( jObj, i, jObj )
       }
    };
    

    See this example on jsFiddle with performance measurement.

    【讨论】:

    • 那么this 也可以吗? :) 另外,我现在需要进行一些分析以了解哪个更快。
    • @badp:是的。我认为obj[i]obj.slice(i, i+1) 没有太大区别...请参阅此处的示例jsfiddle.net/m7pKk
    • @BrunoLM,实际上$(".foo")[i] 返回一个DOM 对象,而$(".foo").eq(i) 给出一个jQuery 对象。这里的重点是,考虑到通过切片实现eq,我几乎相信$($(".foo")[i]) 会更快......
    • 确实,采用 BrunoLM 的 each2 实现并将其更改为仅调用一次 eq(i) 大约一半的执行时间 -- source
    • @BrunoLM:你确定列表总是包含 jQuery 对象吗?如果是这样,那就回到最初的问题:为什么 jQuery 不首先以这种方式实现它?
    【解决方案3】:

    每次迭代都会对性能造成明显影响。每次迭代创建一个新的 jQuery 对象会慢得多,并且在大型集合中可能会很明显。很多时候,您不需要包装对象的额外便利,尤其是在访问单个属性或属性时。您经常会看到像 $(this).is(":checked") 而不是 this.checked 这样的循环浪费代码。

    不过,除此之外,我想说这是因为它有意义。一个 jQuery 对象通常表示一个 DOM 对象的集合,这些对象的大小可以是任意数量。有时 jQuery 纯粹是为了它的选择器支持和事件绑定而使用的,除此之外没有太多。在迭代更多元素的集合时,返回包含单个元素的集合没有多大意义。返回您更可能需要的单个项目(DOM 元素)更有意义,然后如果您想要添加的功能,可以使用 jQuery 包装它。这也使它与迭代 NodeList 和其他类型的集合保持一致。

    【讨论】:

      【解决方案4】:

      我相信由于 jQuery 使用包装器对象,第一种方法使用原始包装器,并且只删除包装器中除第一个元素之外的所有元素,从而使其保持为 jQuery 对象。

      但是,如果他们要为 each() 函数的每个节点提供一个 jQuery 对象,他们将产生为每个节点创建包装器的开销。而且由于您不一定需要该包装器对象,因此会产生不必要的开销。

      【讨论】:

        【解决方案5】:

        可能是因为,在您的示例中,甚至没有理由使用 each。而不是:

        $(".foo").each( function(idx, obj) {
          $(obj).text("hi!");
        )
        

        只需使用:

        $(".foo").text("hi!");
        

        在处理 jQuery 集合时,一切都自动复数。

        【讨论】:

        • 我需要在每次点击时调用一个比 .text() 调用更复杂的函数 :)
        • 但这正好解决了问题——如果您需要为集合中的每个项目做一些独特的事情,那么将它们与“.foo”一起选择是否有意义?
        • 是的,如果我想对每个“.foo”中的值应用一个数学函数。相同的功能,不同的输入,不同的输出。
        • 啊,在这种情况下,我建议将一个函数传递给 text(),但另一位发帖人可能已经建议了。我在想你可能会像$(this).text($(this).hasClass("selected") ? "selected" : "") 或其他一些愚蠢的事情,在这种情况下我会建议只做 jQuery(".foo.selected") 或其他什么。
        【解决方案6】:

        你看到.eachjQuery.each了吗

        您应该能够做到以下几点:

        $('li').each(function(index) {
            alert(index + ': ' + $(this).text());
        });
        

        根据第一个链接。试试$(this) 而不是$(obj)

        【讨论】:

        • 这并没有什么不同。我仍然需要制作 jQuery 对象。
        【解决方案7】:

        试试

        $(".foo").each( function(idx, obj) {
          $(this).text("hi!")
          }
        )
        

        【讨论】:

        • $() 构造函数仍然存在。