JavaScript 的数据访问性能优化
Categories: Study
JavaScript 中有四种基本的数据存取位置:直接量、变量、数组元素和对象成员,和其他语言一样,数据的存储位置会很大的影响其读取速度,那么该如何确定数据的存储位置来提高性能呢?我们一个一个来理解这个问题。
四种基本数据存储位置
- 直接量:直接量只代表自身,不存储特定位置,可想而知,使用直接量的访问速度会比其他会要快一点。JavaScript 中的直接量有:字符串,数字,布尔值,对象,数组,函数,正则,以及 null 和 undefined 值。
- 变量:我们用 var 定义的数据存储单元。
- 数组元素:存储在 JavaScript 数组对象内部,以数字作为索引。
- 对象成员:存储在 JavaScript 对象内部,以字符串作为索引。
总体来说,在 JavaScript 中访问直接量和局部变量的速度要快于数组项和成员对象的速度,那么如何来尽量使用直接量和局部变量,减少数组项和成员对象的使用呢?这里《高性能的 JavaScript》里面有这些建议:
管理作用域
作用域链和标识符解析
什么是作用域?当代码在一个环境中执行时候,会创建函数对象的一个作用域链,作用域链的用途是保证对执行环境有权访问的所有函数的有序访问,当前执行的代码所在环境的变量对象在作用域的头部。作用域中的下一个变量对象来自包含环境,而再下一次环境变量则来自下一个包含环境,一直延续到全局执行环境,也就是最后一个执行环境,全局对象包含 window、navigator 和 document 这些。
标识符的解析过程可以理解是对于作用域链的搜索过程,搜索过程从头部开始,假如找到了就使用这个标识符对应的变量,停止搜索,假如没有找到就搜索作用域中下一个对象,直到找到,假如到了全局执行环境都没有找到那就找不到了,也就是那个没有被定义。
标识符解析的性能
上面说了,标识符解析是一个搜索的过程,可想而知,当我们需要的变量在第一次搜索的时候就找到了,那么时间也就会是最短的,也就是说,一个标识符的位置越深,它的读取速度就越慢,不过又说回来,V8 引擎已经优化了这个问题,但是在没有被优化的 JavaScript 引擎的浏览器中,建议尽可能使用局部变量。还有一个好的建议:如果跨作用域的值在函数中被引用一次以上,那么就把它存到局部变量里面。
改变作用域链
with 语句和 try-catch 这两个语句都可以用来改变作用域链。
当代码执行到 with 语句时候,运行期上下期的作用域链临时被改变了,一个新的可变对象被创立,它包了参数指定的对象所有属性,这个对象被推入所用域链的头部,这意味着函数所有的局部变量现在都处于第二个所用域对象中,因此访问对象更高了。
对于 try 语句也有同样的效果,当 try 中代码块发生错误时候执行过程会自动跳转到 catch 子句中,然后把异常对象推入一个可变对象并置于作用域的头部,这样就出现了和 with 语句一样的问题(所有的局部变量现在都处于第二个所用域对象中)。
以上这两种方法得小心使用,或者说应该避免使用。
成员对象
- 嵌套的对象成员会明显影响性能,尽量少用。
- 属性或方法在原型链中位置越深,访问他的速度也越慢。
- 通常来说,在函数中如果要多次读取同一个属性对象,最佳做法是将属性值保存到局部变量中。局部变量能用来替代属性以避免多次查找带来的性能开销,特别是在使用嵌套成员对象时候,这样会明显改变执行速度。