Search code examples
javascriptweb-performance

Is it expensive to read properties from window object?


For example, let's say we have two versions of lazyload (see code below). In terms of performance, is versionII better than version I?

const imgs = document.querySelectorAll('img'); 
window.addEventListener('scroll' , lazyload);

// version I 
function lazyload() { 
  imgs.forEach((img) => { 
    if (img.offsetTop < window.innerHeight + window.pageYOffset) { 
      img.src = img.dataset.src; 
    }
  }
}

// version II
function lazyload() { 
  const innerHeight = window.innerHeight; 
  const pageYOffset = window.pageYOffset; 
  imgs.forEach((img) => { 
    if (img.offsetTop < innerHeight + pageYOffset) { 
    img.src = img.dataset.src; 
  }
}

Solution

  • Your specific question:

    I'll rephrase your specific question like this:

    Is it costly to access window.innerHeight and/or window.pageYOffset?

    It can be. According to Paul Irish of the Google Chrome Developer Tooling team:

    All of the below properties or methods, when requested/called in JavaScript, will trigger the browser to synchronously calculate the style and layout*. This is also called reflow or layout thrashing, and is common performance bottleneck.

    ...

    window

    window
    window.scrollX, window.scrollY
    window.innerHeight, window.innerWidth
    window.getMatchedCSSRules() only forces style
    

    -- What forces layout / reflow (emphasis mine)

    At the bottom of that document, Paul indicates the layout reflow will only occur under certain circumstances. The portions below (with my added emphasis) answer your question better and more authoritatively than I could.

    • Reflow only has a cost if the document has changed and invalidated the style or layout. Typically, this is because the DOM was changed (classes modified, nodes added/removed, even adding a psuedo-class like :focus).
    • If layout is forced, style must be recalculated first. So forced layout triggers both operations. Their costs are very dependent on the content/situation, but typically both operations are similar in cost.
    • What should you do about all this? Well, the More on forced layout section below covers everything in more detail, but the
      short version is:
      1. for loops that force layout & change the DOM are the worst, avoid them.
      2. Use DevTools Timeline to see where this happens. You may be surprised to see how often your app code and library code hits this.
      3. Batch your writes & reads to the DOM (via FastDOM or a virtual DOM implementation). Read your metrics at the begininng of the frame (very very start of rAF, scroll handler, etc), when the numbers are still identical to the last time layout was done.

    Changing the src attribute is probably sufficient to "invalidate the style or layout." (Although I suspect using something like correctly-dimensioned SVG placeholders for lazy-loaded images would mitigate or eliminate the cost of the reflows.)

    In short, your "version I" implementation is preferable and has, as far as I can tell, no real disadvantages.

    Your general question

    As shown above, reading properties from the window object can be expensive. But others are right to point out a couple things:

    1. Optimizing too early or too aggressively can cost you valuable time, energy, and (depending on your solution) maintainability.
    2. The only way to be certain is to test. Try both versions of your code, and carefully analyze your favorite dev tool's output for performance differences.