Caching vs .querySelector

Caching vs .querySelector

本文关键字:querySelector vs Caching      更新时间:2023-09-26

我们很早就被教导要缓存选择器。他们都说,只要做一个查找。

我想知道这个想法如何与.querySelector一起工作。

假设我想抓取我刚刚点击的元素下方的元素。js 将是这样的:

this.querySelector('.toggle-this-content').classList.toggle('open')

所以我点击一个按钮,它会切换它下面的内容以打开。这是一个昂贵的选择吗?我可能应该正确地进行委派,所以我可以说:

toggleThisContent.classList.toggle('open')

在我正在使用的函数中,但我想知道使用 .querySelector 是不好的做法还是 A-Ok。

你用了"昂贵"这个词,你用了"坏做法"这个词。但是,这两者并不是同义词。有些东西可能很昂贵,但很好的做法;或便宜但不好的做法。或者,您可以重新定义"昂贵"一词,不仅意味着某些东西需要大量的CPU周期,而且需要大量的人力周期来编写,读取,调试,扩展和维护。

请考虑以下事项:

function foo(sel) { 
  if (document.querySelector(sel)) document.querySelector(sel).classList.add('bar');
}

正如你所建议的,大多数有经验的程序员可能更喜欢

function foo(sel) { 
  let elt = document.querySelector(sel);
  if (elt) elt.classList.toggle('bar');
}

但实际上原因与性能无关,尽管这个版本的性能可能略好一些(我的意思是,很可能快了几十微秒)。这是可取的,因为它不是重复的。程序员意图"检索sel选择的元素"的单一方面在单个querySelector调用中只表达一次。代码更短,不易出现拼写错误。它似乎更具可读性。

让我们以另一种情况为例,其中特定querySelector在代码的不同部分完成,因此

function x1() {
  document.querySelector('.c1').classList.toggle('x1');
}
function x2() {
  document.querySelector('.c1').classList.toggle('x2');
}

在这种情况下,安排仅执行单个querySelector('.c1')需要计算并将元素保存在两个调用中的任何一个可用的位置 - 仅作为一个简单的示例:

let x = function() {
  let elt = document.querySelector('.c1');
  return { 
    x1: function() { elt.classList.toggle('x1'); },
    x2: function() { elt.classList.toggle('x2'); }
  };
}();
x.x1();

您已经成功地将调用次数减少到 querySelector 次,这将为您节省几十微秒,但代价是代码稍微复杂一些。

因此,在这种情况下,在其他条件相同的情况下,如果查看原始代码(两个单独的函数调用)并询问将其重构为第二个片段(IIFE)有多重要,答案将是"几乎没有"。

这并不是说在更高层次上重构它不是一个好主意。您正在参与将 DOM 操作代码过于紧密地绑定到特定类(或在其他常见情况下为 ID)的反模式。在这种模式中,程序员使用 ID 和类作为引用 DOM 元素的一种变量名称,并且在他们需要获取实际元素的任何地方,使用 getElementByIdgetElementsByClassNamequerySelector 从巨大的全局 DOM 命名空间中一遍又一遍地检索它。最好使用存储在变量中的 DOM 元素来引用 DOM 元素,并通过这些变量的 ID 或类将这些变量映射到特定元素,以在代码的某一部分中尽可能小的外围应用区域。这不是性能问题,而是良好的代码结构问题。这样,例如,您可以在 HTML 中重命名类c1,并且代码中只有一个需要更改的位置。当然,这需要安排您的代码,以便变量在需要时可用。但这实际上与安排代码不使用全局变量没有什么不同 - 因为querySelector('.c1')是一种全局变量 - 无论如何你都应该这样做。