JavaScript数据访问对性能的影响
1.前言
在 JavaScript作用域链分析 中介绍了函数的作用域链。在函数运行后,会生成激活对象,每遇到一个变量,js都要根据作用域链来决定从哪里获得和存储数据。
此过程搜索运行函数的作用域链,搜索工作从运行函数的激活目标之作用域链的前端开始,如果找到了,那么就使用这个变量;如果没找到,搜索工作将进入作用域链的下一个对象。此过程持续运行,直到变量被找到,或者没有更多对象可用于搜索,这种情况下标识符将被认为是未定义的。
而这种搜索过程影响了 JavaScript 的运行性能。
2.全局变量和局部变量分析
在一个函数运行的作用域链上,一个变量所处的位置愈深,它的读写速度就愈慢。因此,局部变量的访问速度是最快的,而全局变量的访问通常是最慢的。
可以看一下比较结果
代码
1 | var global = 'hello' |
运行结果
IE8下
chrome39下
在IE下运行多次基本上都是局部变量的速度快。而在Chrome下多次运行,两者的速度相差无几,而且速度快了两个数量级,这是由于Chrome优化js引擎的结果。
一般来说,对于浏览器,变量所处位置愈深,它的速度就愈慢。而在现代浏览器中,由于JavaScript引擎被优化,这种性能损失几乎就没有了。但是在那些没有优化过的浏览器里(IE),依然要注意这一点。
通过以上信息,在没有优化 JavaScript 引擎的浏览器中,最好尽可能使用局部变量。一个好的经验法则是:用局部变量存储本地范围之外的变量值。
下面举个栗子:
1 | function initUI(){ |
此函数包含三个对 document 的引用,document 是一个全局对象。搜索此变量,必须遍历整个作用域链,直到最后在全局变量 window 对象中找到它。我们可以将document这个全局变量的引用存储在一个局部变量里,在下面的代码中使用这个全局变量:
1 | function initUI(){ |
现在访问全局变量的次数是 1 次,而不是 3 次。用 doc 替代 document 更快,因为它是一个局部变量。通过这个简单的函数,我们可以发现,当变量的数量增加时,性能会的到明显的提升。
3.闭包中变量分析
闭包允许函数访问局部范围之外的数据。举一个栗子:
1 | function f1(){ |
可以看到,函数f1的激活对象是函数f2和变量n,当函数f2运行时,变量n读取的是f1的激活对象里面的n变量。这是闭包的一个性能关注点,当经常访问函数范围之外的变量时,每次访问都会导致一些性能损失。
根据上文中对局部变量和全局变量的分析,可以将范围之外的变量存入局部变量中,然后直接访问局部变量即可。
4.总结
JavaScript 中变量的访问是按照作用域链的规定来搜索的,在该过程中会造成性能损失。在未优化的浏览器(PS:特别是IE)中需要特别关注这一点。在局部变量、范围外变量和全局变量中读取中,可以尽量将之存入局部变量中,使用局部变量访问。