Why is accessing a variable using window.variable slower? Why is accessing a variable using window.variable slower? google-chrome google-chrome

Why is accessing a variable using window.variable slower?


Javascript is terrible for optimization because of eval (that can access the local frame!).

If however the compilers are smart enough to detect that eval plays no role then things can get a lot faster.

If you only have local variables, captured variables and global variables and if you can assume no messing up with eval is done then, in theory:

  • A local variable access is just a direct access in memory with an offset from the local frame
  • A global variable access is just a direct access in memory
  • A captured variable access requires a double indirection

The reason is that if x when looked up results in a local or in a global then it will always be a local or a global and thus it could be accessed directly say with mov rax, [rbp+0x12] (when a local) or mov rax, [rip+0x12345678] when a global. No lookup whatsoever.

For captured variables things are slightly more complex because of lifetime issues. On a very common implementation (with captured variables wrapped up in cells and cell address copied when creating closures) this will require two extra indirection steps... i.e. for example

mov rax, [rbp]      ; Load closure data address in raxmov rax, [rax+0x12] ; Load cell address in raxmov rax, [rax]      ; Load actual value of captured var in rax

Once again no "lookup" needed at runtime.

All this means that the timing you are observing is a consequence of other factors. For the mere variable access the difference between a local, a global and a captured variable are very tiny compared to other issues like caching or implementation details (e.g. how the garbage collector is implemented; a moving one for example would require an extra indirection for globals).

Of course accessing a global using the window object is another matter... and I'm not very surprised it takes longer (window is required to be also a regular object).


One thing to note is that testing micro-optimizations is no longer easy to do because the JS engine's JIT compiler will optimize code. Some of your tests that have extremely small times are probably due to the compiler removing "unused" code and unrolling loops.

So there's really two things to worry about "scope chain lookup" and code that impedes the JIT compiler's ability to compile or simplify the code. (The latter is very complex so you'd be best to read up on a few tips and leave it at that.)

The issue with scope chain is that when the JS engine encounters a variable like x, it needs to determine whether that is in:

  • local scope
  • closure scopes (such as that created by IIFE)
  • global scope

The "scope chain" is essentially a linked list of these scopes. Looking up x requires first determining if it is a local variable. If not, walk up any closures and look for it in each. If not in any closure, then look in the global context.

In the following code example, console.log(a); first tries to resolve a in the local scope within innerFunc(). It doesn't find a local variable a so it looks in its enclosing closure and also doesn't find a variable a. (If there were additional nested callbacks causing more closures, it would have to inspect each of them) After not finding a in any closure, it finally looks in the global scope and does find it there.

var a = 1; // global scope(function myIife(window) {    var b = 2; // scope in myIife and closure due to reference within innerFunc    function innerFunc() {        var c = 3;        console.log(a);        console.log(b);        console.log(c);    }    // invoke innerFunc    innerFunc();})(window);


When I run your code snippet in Chrome every alternative takes a few milliseconds, except for directly accessing window.x. And unsurprisingly using object properties is slower than using variables. So the only question to answer is why is window.x is slower than x and even slower than anything else.

Which leads me to your premise that x = 1; is the same as window.x = 1;. And I'm sorry to tell you that's wrong. FWIW window is not directly the global object, it's both a property of it and a reference to it. Try window.window.window.window ...

Environment records

Every variable has to be "registered" in an environment record and there are two primary kinds: Declarative and object.

Function scope uses a declarative environment record.

Global scope uses an object environment record. And that means that every variable in this scope is also a property of an object, in this case the global object.

It also kind of works the other way round: Every property of that object can be accessed through an identifier with the same name. But that doesn't mean you are dealing with a variable. The with statement is another example of using an object environment record.

The difference between x=1 and window.x = 1

Creating a variable is not the same as adding a property to an object, even if that object is an environment record. Try Object.getOwnPropertyDescriptor(window, 'x') in both cases. When x is a variable then the property x is not configurable. One consequence is that you cannot delete it.

When we only see window.x we don't know if it's a variable or a property. So without further knowledge, we simply cannot treat it as a variable. Variables live in the scope, on a stack, you name it. A compiler could check if there's also a variable x but that check would probably cost more than simply doing window.x = window.x + 1. And don't forget that window only exists in Browsers. JavaScript engines also work in other environments which may have a differently named property or even none at all.

Now why is window.x so much slower on Chrome? Interestingly in Firefox it's not. In my test runs FF is much faster and the performance of window.x is on par with every other object access. The same is true for Safari. So it may be a Chrome issue. Or accessing an environment record object is slow in general and the other browsers simply optimize better in this specific case.