哪种方法的性能更好,检查类还是添加类?

3

如果你有一个在滚动事件上触发的函数,哪个更好呢?

  1. 检查是否已经添加了类,如果没有则添加
  2. 不进行任何检查,每次需要时都添加类
$(document).on('scroll', function () {
   var scrollTop = $(this).scrollTop();
   if (scrollTop > 50) {
      if (!$("nav .branding").hasClass("collapse"))
         $("nav .branding").addClass("collapse");
   } else {
      if ($("nav .branding").hasClass("collapse"))
         $("nav .branding").removeClass("collapse");
   }
});

或者

$(document).on('scroll', function () {
   var scrollTop = $(this).scrollTop();
   if (scrollTop > 50) {
      $("nav .branding").addClass("collapse");
   } else {
      $("nav .branding").removeClass("collapse");
   }
});

在第一种情况下,会进行额外的检查,但在其他操作中可能会有更加深入的操作。


无论如何,如果您关心性能,可以做一件事,就是将任何选择器都放在滚动事件之外。只需在滚动事件处理程序外部创建一个存储.branding元素的变量即可。 - xec
@user2740744,如果您对此问题的解决方案不满意,并希望对其进行更多讨论,请参见https://dev59.com/yms05IYBdhLWcg3wLO2Q#32183320。我在那里特别回答了您的问题。 - Igwe Kalu
5个回答

11

如果您关心性能,有几件事情可以/应该做:

1. 缓存您的选择器:

DOM交互是昂贵的,在您的情况下,您多次调用$("nav .branding")

将其存储在变量中,如下所示:var $branding = $("nav .branding")

2. 使用纯JavaScript处理类别

取决于浏览器支持

var branding = document.querySelector('nav .branding');

if (scrollTop > 50) {
  if (!branding.classList.contains("collapse")) {
    branding.classList.add("collapse");
  }
} else {
  if (branding.classList.contains("collapse")) {
    branding.classList.remove("collapse");
  }
}
请注意,并非所有的浏览器都支持 classList 属性,在 MDN 上可以找到有关兼容性和 polyfill 的信息
至于您最初的问题:jQuery 的 addClass 已经内置了检查类是否已存在的功能,因此如果您不先使用 hasClass,则最好使用它以避免冗余。但请记住,addClassclassList 慢约 70%

2
@Alex的回答是基于性能的。 - Deblaton Jean-Philippe
2
这是目前为止最好的答案,因为它解决了唯一的实际性能问题(在滚动事件中反复选择运行),当然,原生版本优于任何非本机API。@Alex有一点:您不应该检查类的存在,classList API已经足够聪明了 :) - xec
2
@Alex,欢迎随时为答案做出贡献 :) - Patsy Issa
4
就我而言,这个问题标记了 JavaScript,解决问题的方法很多,这只是其中之一,有一些统计数据。这里的交流有点太多了。 - Patsy Issa
2
@Alex 如果你觉得答案不够完整,可以添加内容或者提供更全面的回答。 - Patsy Issa
显示剩余5条评论

2

像你说的那样,.hasClass() 是一个额外的检查,会占用浏览器的内存。而 .addClass() 首先进行内部检查,然后只在特定类不存在时添加。

因此,明显地,使用 .addClass().removeClass() 比使用 .hasClass() 进行首次检查更加高效。基本上,使用 .hasClass() 是一种多余的努力。

这是一个片段,证明了 .addClass() 已经检查了现有类或不重复类名:

$(function () {
  $("#classCheck").addClass("class");
  $("#classCheck").addClass("class");
});
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<div id="classCheck"></div>

请大家将上述代码片段视为 .addClass() 不会重复添加类名的证明。
在控制台中简单检查即可发现,多次调用 addClass 并传入相同的类是安全的。
具体而言,你可以在源代码中找到以下检查:链接
if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
  setClass += classNames[ c ] + " ";
}

同时,正如Alex在评论中指出的那样,如果您存储jQuery选择器对象,可以提高性能。不要多次使用$("nav .branding"),而是可以使用$nav_branding = $("nav .branding") 并使用变量代替。


“addClass” 根据他们的文档,内部不执行检查。如果他们在代码中执行了该检查,请在此处添加。 - Mike Perrenoud
@AndreasMøller 你的评论毫无意义!addClass有一个内置检查,就像hasClass一样,因此使用hasClass会导致冗余。 - Alex
@MichaelPerrenoud,请查看已添加的代码片段作为证明。 - Praveen Kumar Purushothaman
2
@PraveenKumar 如果你提到了关于节流和选择存储在变量中的点,你的答案就非常棒了 :) - Alex
@Alex,你介意编辑我的答案并添加这些要点吗? - Praveen Kumar Purushothaman
显示剩余3条评论

2
第二种方法更好,因为根据现有类别调用addClass / removeClass会产生不显眼的结果。
也就是说,如果调用addClass并且类已经存在,则不会再次添加。
删除时同样如此。 如果类不存在,则什么也不会发生。

0

第一种解决方案更快,但差别不大。(即:应该无关紧要) https://github.com/jquery/jquery/blob/master/src/attributes/classes.js

如果您想要平滑的性能,我建议对函数进行节流。

即:https://remysharp.com/2010/07/21/throttling-function-calls

节流确保函数仅在每 n 毫秒调用一次。

$(document).on('scroll', throttle(500, function () {
  var scrollTop = $(this).scrollTop();
  if (scrollTop > 50) {
    if (!$("nav .branding").hasClass("collapse")){
      $("nav .branding").addClass("collapse");
    }
  } else {
    if ($("nav .branding").hasClass("collapse")) {
      $("nav .branding").removeClass("collapse");
    }
  }
}));   

编辑:

正如@kitler所提到的,存储您的DOM元素的引用是一个好建议。 这可以防止jQuery每次都要查找该元素。


-2
我会选择选项2,因为在选项1中,您只是为了测试类是否可用而进行jQuery DOM选择,这应该是所有操作中最昂贵的部分。
话虽如此,最好只在函数外部将DOM对象的引用存储一次,并仅对该引用进行操作。

给那些点踩的人 - 请留下评论,说明为什么要点踩。 - connexo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接