高性能javascript笔记:数据的存储与访问性能优化

  在javascript中,数据的存储位置对代码的整体性能有着重要的影响。有四种数据访问类型:直接量局部变量数组项对象成员。直接量和局部变量访问的都非常快,数组项和对象成员的访问速度就有待优化了。

  局部变量也就可以理解为在函数内部定义的变量,很明显访问局部变量要比域外的变量要快,因为它位于作用域链的第一个变量对象中(关于作用域链的介绍可以阅读这篇文章)。变量在作用域链的位置越深,访问所需要的时间就越长,全局变量总是最慢的,因为它们位于作用域链的最后一个变量对象。

  每种数据类型的访问都需要付出点性能代价,对于直接量和局部变量基本都能消费得起,而访问数组项和对象成员则要代价高点。下图显示了不同浏览器,分别对这四种数据类型进行了200'000次操作所用的时间。

由上图可以看出,要想优化代码的性能,那么尽量使用直接量和局部变量,限制数组项和对象成员的访问次数(将对象成员用一个局部变量来保存)。

  首先我们需要了解一下对象成员的访问过程。其实函数就是一个特殊的对象,所以对象成员的访问跟函数的内部变量的访问都差不多,都是基于链的查找,前者是原型链,后者是作用域链,只是怎么个链法有点差别而已。

  对象成员包含属性和方法,如果该成员是一个函数就称为方法,否则就称为属性。

  Javascript中的对象是基于原型(原型本身就是一个对象)的,原型是其他对象的基础。当你实例化一个Object对象或其它JS的内置对象时(var obj=new Object() or var obj={}),实例obj的原型由后台自动创建,浏览器FF,safari,Chrome可通过obj.__proto__属性(等同于Object.prototype)可以访问到这个原型,也正是因为这个原型,每一个实例都能共享原型对象的成员。如:

var book = {
   name:"Javascript Book",
   getName: function(){
     return this.name;
   }
};
alert(book.toString()); //"[object Object]"

此代码中,book对象有两个私有成员,分别是属性name和方法getName。book对象并没有定义成员toString,但调用了也没有抛出错误,原因是book对象继承了原型对象的成员。book对象与原型的关系如下:

访问book对象成员toString的过程是这样的,当book.toString()被调用时,后台对成员进行名为"toString"的搜索,首先从实例book本身开始,如果在book发现名为"toString"的成员,则搜索结束,否则继续向__proto__指向的原型对象搜索,如果在Object.prototype都找不到该成员,则表示该成员未定义。通过这种方式,book就可以访问它的原型对象所拥有的每个属性或方法。

  对象的另一高级用法就是模拟类和继承类,我喜欢叫这样用法的对象为对象类。继承对象类主要就是依靠原型链来完成的,这个知识点太多需要另外详细说明。通过上面的对象成员搜索过程,访问对象成员的速度,随着原型链的越深,搜索的速度就越慢。下图就显示了对象成员在原型链中所处的深度与访问时间的关系:

由上图可清楚的知道,每深入原型链一层都会增加性能的损失,所以像那种遍历对象成员的操作开销很大。还有另外一种常用且损耗性能的做法就是嵌套对象成员(如window.location.href),像这种最好的做法就是减少点的次数了。比如location.href就比window.location.href快。

好了,啰啰嗦嗦那么多都不知道自己说什么。目前自己的博文水平还不是很高,有时候就算自己理解了也很难表达出来,别误导别人就好了。真正总结起来就一句话:一个属性或方法在原型链的位置越深,访问它的速度就越慢。解决办法就是:将经常使用的对象成员,数组项和域外的变量存入局部变量中,然后访问这个局部变量。

posted @ 2012-06-29 15:46  无赖君子  阅读(1684)  评论(7编辑  收藏  举报