I recently read "High Performance JavaScript" (2010) which states:
The deeper into the execution context’s scope chain an identifier exists, the slower it is to access for both reads and writes. Consequently, local variables are always the fastest to access inside of a function, whereas global variables will generally be the slowest. Keep in mind that global variables always exist in the last variable object of the execution context’s scope chain, so they are always the furthest away to resolve.
Given the substantial optimizations in JavaScript engines over the past decade, I wonder is this statement still accurate in 2024?
let globalVar = 0;
function testVariables() {
let localVar = 0;
let globalStartTime = performance.now();
for (let i = 0; i < 5000000; i++) {
globalVar += 1;
}
let globalEndTime = performance.now();
let localStartTime = performance.now();
for (let i = 0; i < 5000000; i++) {
localVar += 1;
}
let localEndTime = performance.now();
console.log(`Global variable access time: ${globalEndTime - globalStartTime} ms`);
console.log(`Local variable access time: ${localEndTime - localStartTime} ms`);
}
testVariables();
The output is:
Global variable access time: 25.80000001192093 ms
Local variable access time: 11.400000035762787 ms
It is surely that global variables take more time to be accessed than local variables, but I am still not sure that whether scope chain traversal is the main reason behind this. Perhaps there are other reasons?
2
Answers
I think another reason is that if you have variable in the global namespace, then external can change that variable and it will force the interpreter to reload the value each time through the loop.
Hence makes it slower than local variable
When you access a variable in an outer scope (not always global, an engine should make a lookup), so it’s slower. Not sure why the global scope isn’t updated only after the loop completes, since there’re no reads in the global scope possible until the loop completes (maybe it’s not possible to optimize).
With object references it’s different, since no object reference is mutated and objects are allocated on the heap, so no actual difference when you mutate them.
And benchmarking:
Open in the playground