JavaScript variable performance
One of the most common pieces of advice regarding JavaScript performance is to favor local variables over global variables whenever possible. This advice has been around for at least as long as I’ve been working web development (nine years) and is never questioned. The basis for this theory is the way that JavaScript handles scoping and identifier resolution.
The first thing to remember is that functions are objects in JavaScript, so creating a function actually means creating an object to represent it. Each function object has an internal property called [[Scope]] that is initialized with information about the scope in which the function was created. The [[Scope]] property is actually a list of variable objects accessible from the function’s containing scope. When you create a global function, it’s [[Scope]] property has only the global object in the list; when a function is created inside of a global function, the global function’s activation object is at the front of [[Scope]] and the global object is second.
When a function is executed, an activation object is created and then has a scope chain associated with it. The scope chain is used for identifier resolution and is created in two steps:
- The objects in the function object’s
[[Scope]]property are copied into the scope chain in the same order. - A new activation object is created with variables for the executing function. This object contains the definitions for
this,arguments, and local variables (including named arguments), and is pushed onto the front of the scope chain.
When an identifier is encountered, the execution context’s scope chain is searched for an identifier with a matching name. The search begins at the first object in the scope chain, the function’s activation object, and continues towards the global object until the variable is found (or ends in an error if the variable is never found). This is the way that ECMA-262 describes the behavior of function execution and identifier resolution and, as it turns out, is the way that many JavaScript engines have implemented the language. Note that ECMA-262 does not mandate this structure, it is merely provided as a description of the appropriate functionality.
Given this description of identifier resolution, it makes sense that local variables should have faster identifier resolution than variables from other scopes because the search for a matching name is much shorter. But how much faster? In order to answer this question, I set up a series of tests using variables of different scope-depth.
My first tests involved writing a simple value to variables (literally, the value 1). The results were interesting.
From the results, it’s clear that there is a performance penalty associated with deep searches for identifier resolution. Each increase in identifier depth shows an increase in execution. Internet Explorer, not surprisingly, is the worst of the class (although IE 8 does show some improvement). The notable exceptions in this case are Google Chrome and the latest nightly WebKit as their variable access times remain roughly constant even as identifier depth increases. This can be attributed to their next-generation JavaScript engines, V8 and SquirrelFish respectively. These engines perform optimizations to run code faster, and clearly, these optimizations make variable access much faster than others. Opera performed admirably, being faster than IE, Firefox, and current Safari versions but slower than the V8 and Squirrelfish-powered browsers. Firefox 3.1 Beta 2 was somewhat surprising as local variable writes were very fast but performance degraded significantly once the variable was out of the local scope. It’s worth noting that I was using the default settings and therefore didn’t have tracing turned on.
Those results were for variable writes, and I wondered if the performance for variable reads would be any different. Variable reads turned out to be somewhat faster than writes even though the same trends emerged.
Once again, Internet Explorer and Firefox are the slowest, with Opera showing respectable performance. And once again, Chrome and the latest WebKit nightly show flat performance based on identifier depth. Also notable is the same strange jump in Firefox 3.1 Beta 2’s variable access times once you’re no longer dealing with local variables.
One interesting thing I found in my research is that Chrome has a performance penalty for accessing global variables. The access time for global variables remains constant regardless of identifier depth but that value is 50% higher than the amount of time it takes to access local variables with the same identifier depth.
What does all of this mean? It basically means that my research supports that advice that you should use local variables whenever possible. In almost all browsers, local variables are faster for both reading and writing than out-of-scope variables including globals. You can take advantage of this knowledge in several ways:
- Watch the variables being used in a function. If you notice a function using an out-of-scope variable more than once, store it in a local variable and use that instead. You’ll be reducing the number of out-of-scope identifier resolutions for that variable to one. This is especially important for global variables, which are always the last object in the scope chain.
- Avoid using the
withstatement. It temporarily augments the execution context’s scope chain by adding a new variable object to the front. This means that local variables actually move to an identifier depth of two during execution of thewith, imposing a performance penalty. - Avoid using
try-catchif you know an error will always occur. The catch clause augments the scope chain in the same manner as thewithstatement. There is no penalty for running code in thetryportion of the code, so it is still advisable to usetry-catchfor dealing with unexpected errors.
If you’d like a bit more discussion around this topic, I gave a short talk at last month’s Mountain View JavaScript Meetup. The slides are available on SlideShare and a video of the entire night is available on YouTube (I’m at about the 11 minute mark). The video is especially entertaining as my laptop was misbehaving the entire time.
Translations
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of Yahoo!, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
You can leave a response, or trackback from your own site.






17 Comments
Hi!
For firefox 3.1, did tracemonkey was enabled ?
Jean-Philippe Martin on February 10th, 2009 at 11:02 am
@Jean-Phillipe I used Firefox 3.1 Beta 2 out-of-the-box with default settings, so tracing wasn’t enabled.
Nicholas C. Zakas on February 10th, 2009 at 2:45 pm
Thanks for this, Nicholas. I’ll be sure to always add a level of depth for Firefox when I have variables scoped 3 levels deep.
(kidding, of course) How in the world did that happen, anyways?
Jerry Su on February 10th, 2009 at 4:41 pm
My hunch is that they super-optimized local variable access and left everything else alone.
Nicholas C. Zakas on February 10th, 2009 at 10:30 pm
[...] at NCZOnline there is a good post about the performance of variable reads and writes. Is it faster to use local vars or global [...]
My Bad Attitude » JavaScript variable performance on February 11th, 2009 at 3:34 pm
Thanks for the write-up, Nicholas.
Your last bullet-point has intrigued me quite a bit.
try...catchblocks are usually regarded as expensive in JS, but as you mention and the specs confirm, thecatchclause is the culprit here.So if my reasoning is correct, using
try...catchblocks defensively should have a negligible performance impact.Would love to see that confirmed by benchmarks (hint, hint).
Tobie Langel on February 12th, 2009 at 8:41 am
Out of curiosity did you do any benchmarking for access time of an objects properties. For example how does accessing this.varName perform versus scope variables?
Bryan J Swift on February 17th, 2009 at 12:25 pm
Hi Bryan - Yes, in-scope variables are much faster than property access.
Nicholas C. Zakas on February 18th, 2009 at 1:45 am
[...] C. Zakas于2009年2月10日在个人网站上发表的《JavaScript Variable Performance》。原文是唯一的正式版,本文是经过原作者(Nicholas C. [...]
在JavaScript中,为什么要尽可能使用局部变量? « 七月佑安 on March 15th, 2009 at 7:00 am
[...] C. Zakas 原文名称:《JavaScript Variable Performance》 翻译作者:明达 [...]
在JavaScript中,为什么要尽可能使用局部变量? - Yaohaixiao’g Blog on April 3rd, 2009 at 2:30 am
[...] Nicholas C. Zakas 于2009年2月10日在个人网站上发表的《JavaScript Variable Performance》。 原文是唯一的正式版,本文是经过原作者(Nicholas C. [...]
L.B.C’s Blog » JavaScript中尽量用局部变量的原因 on May 30th, 2009 at 4:14 am
hi, can you give me the test code (or publish it)? someone else’s test is not the same as you. pls check
http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/09/22/teched-2007-notes-javascript-performance.aspx
Jethro on June 1st, 2009 at 2:51 am
@Jethro, - I’m not sure exactly what that article says, since I don’t know Chinese, but you can try out the identifier depth test for yourself. Enjoy!
Nicholas C. Zakas on June 2nd, 2009 at 12:07 am
[...] 本文译自Nicholas C. Zakas于2009年2月10日在个人网站上发表的《JavaScript Variable Performance》。原文是唯一的正式版,本文是经过原文作者授权的简体中文翻译版。译者在翻译的准确性上做了大量的努力,并承诺译文的内容完全忠于原文,但可能还是包含疏漏和不妥之处,欢迎大家指正。译序和译注的内容是非正式的,仅代表译者个人观点。 [...]
在JavaScript中,为什么要尽可能使用局部变量? « 七月佑安 on June 15th, 2009 at 5:14 am
[...] the second criticism, you’ll note my research on variable performance in JavaScript shows that out-of-scope variables take longer to read to and write from than in-scope [...]
JavaScript minification/compression and performance | NCZOnline on July 7th, 2009 at 9:01 am
[...] Nicholas C. Zakas 于2009年2月10日在个人网站上发表的《JavaScript Variable Performance》。原文是唯一的正式版,本文是经过原作者(Nicholas C. [...]
为什么尽量用局部变量代替全局变量 - 前端征途 on December 11th, 2009 at 2:54 am
After running the identifier-depth test with node.js 0.1.31 and –oprofile=true i’m impressed that v8 produced nearly constant times whereas my firefox total fails
I think this scope-depth performance gap is just a matter of times until its fixed in all engines.
jami on March 21st, 2010 at 10:45 am
Leave a Comment