缓存与.querySelector

时间:2016-02-25 01:58:04

标签: javascript

我们被教会在早期缓存选择器。他们都说,只做一次查找。

我想知道这个想法如何与.querySelector一起使用。

假设我想抓住一个我刚刚点击过的元素。 js类似于:

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

所以我点击一个按钮,它切换下面的内容打开。这是一个昂贵的选择吗?我应该正确地做代表团,所以我可以说:

toggleThisContent.classList.toggle('open')

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

1 个答案:

答案 0 :(得分:5)

您使用“昂贵”一词,并使用“不良做法”一词。但是,这两者并不是同义词。有些东西可能很贵,但是做法很好;或廉价但不好的做法。或者,您可以重新定义“昂贵”这个词,意味着不仅需要大量的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的调用次数减少为1,这将为您节省几十微秒,但代价是更复杂的代码。

所以在这种情况下,所有其他条件相同,如果是查看原始代码(两个单独的函数调用)并询问将其重构为第二个片段(IIFE)的重要性,答案是答案会是,“几乎根本不会”。

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