Javascript性能优化(二)- 数据访问优化
来源:互联网 发布:驱老鼠超声波软件 编辑:程序博客网 时间:2024/06/07 17:19
目录
- 目录
- 数据访问
- Scope
- 优化数据检索
- 原型
- 原型链
- 缓存对象成员的值
- 总结
数据访问
数据的存储位置,关系到代码运行时数据被检索到的速度,JS中有四种数据存储位置:直接量、变量、数组、对象。其中直接量可能比较少听说,其实可以理解为表示匿名函数、匿名对象的一个变量,如var sum = function(a,b){return a+b},sum就是一个函数直接量。
四种数据存储位置中,直接量和局部变量的访问性能微不足道,性能消耗高的是全局变量、数组和对象成员。
Scope
每一个JS函数都是一个对象,函数对象和其他对象一样,拥有编程可以访问的属性和仅供JS引擎使用的属性。其中有一个虚拟属性叫做[[Scope]],[[Scope]]中包含函数作用域中对象的集合(作用域链),它表示当前函数环境可以访问的数据,以链式存在,作用域链的创建改变过程如下:
1.
声明一个函数时函数作用域链中被推入一个可变的全局变量,代表了所有全局范围内的变量。作用域链:A
2.
执行一个函数时会创建一个内部对象(执行完销毁),称其为运行期上下文,运行期上下文会拷贝函数本身的[[Scope]]对象,按照原来的顺序复制到运行期上下文的作用域链中,此时的运行期上下文作用域链:A。然后运行期上下文的作用域链中被推入一个新的对象,名叫‘激活对象’,它存储了所有的局部变量、命名参数、参数集合和this的接口。此时运行期上下文作用域链:B -> A
3.
当函数运行遇到变量时,会检索作用域链,首先会去B中查找是否存在,再去A中查找,显然A中变量访问性能要比B中的更慢。
优化数据检索
- 既然全局变量的访问性能更慢,那么当一个函数中多次访问全局变量的时候,就应该用一个局部变量来进行缓存。
function fun(){ var doc = document; a = doc.getElementById("a"); b = doc.getElementById("b"); c = doc.getElementById("c");}
- 减少使用动态作用域链:with()可以临时改变函数的作用域链,把变量推到作用域链最前端,如下面的demo,运行期上下文作用域链:document -> B -> A,在使用getElementById的时候会先到document对象里查找,速度确实快了。但是问题也显而易见,因为局部变量对象B被推到了第二,所以当访问a,b,c变量的时候,多查找了一次document对象,有时候得不偿失。
// 当使用with时,document临时被放到了作用域链的最前端// 在使用getElemntById时会先到document对象里面寻找function fun(){ with(document){ a = getElementById("a"); b = getElementById("b"); c = getElementById("c"); }}
- try-catch:当运行出错时,程序会自动把异常推到作用域链的最前端,带来性能问题,可以在catch块中运行错误处理函数,将错误对象作为参数传给错误处理函数,catch块中作用域链的改变就没什么影响了。
try{ //do something}catch(e){ handleError(e);}
- 慎用闭包:原因有两个,一个是函数闭包访问外部变量最少经过两次查找。二是如果闭包需要访问外部变量,那么函数运行期的激活对象就会被保存,无法销毁,不仅会消耗更多内存,在IE中还会导致内存泄漏。
原型
- 什么是原型?初学者往往很难理解原型的概念,如果你了解过OOP的编程语言,那么对继承一定不陌生,原型可以简单的理解为被继承的基类。任何时候创建一个实例,这些实例都将自动拥有一个Object作为他们的原型。
- 一个对象拥有两种成员:实例成员和原型成员。实例成员直接存在于实例自身,原型成员则是从原型继承。
- 下面的例子中,cat的实例成员是name和age,原型成员就是cat.proto中的成员属性,即Object中的属性(Object.prototype)。如调用toString方法,会先在cat的实例成员中查找,找不到再去原型成员中查找,所以同样导致性能问题。
// cat._proto_ = Objectvar cat = { name:"xiaohua", age:1}
原型链
- 对象的原型决定了一个实例的类型,默认情况下对象都是Object的实例,但当我们使用构造器创建实例时,就创建了另外一种类型的原型。
- 如下面的例子,
cat._proto_
=Animal.prototype
,Animal.prototype._proto_
=Object
。如果我们调用了toString,搜索路径如下:cat ->cat._proto_
(Animal.prototype) ->cat._proto_.constructor
(Animal) ->cat._proto_.constructor._proto_
(Object)
function Animal(name,age){ this.name = name; this.age = age;}// 通过prototype可以向函数对象添加成员Animal.prototype.sayHello = function(){ console.log("Hello,I am a " + this.name);}var cat = new Animal("cat",1);var dog = new Animal("dog",1);
缓存对象成员的值
由原型链的访问过程可知,当访问的对象成员处在较深处时,访问的代价还是挺大的,所以当多次访问同一个对象成员时,可以进行缓存。
function foo(ele,className1,className2) { // 可以用一个局部变量来存储ele.className // var eleClassName = ele.className; return ele.className == className1 || ele.className == className2;}
总结
- 针对数据访问导致的相关性能问题,主要的解决办法就是对数据进行暂存,比如将全局变量暂存为局部变量,减少作用域链的深入搜索;将实例的属性暂存,减少对原型链的多次深入搜索;另一个就是减少使用动态作用域和闭包。
阅读全文
0 0
- Javascript性能优化(二)- 数据访问优化
- JavaScript数据访问性能优化方案
- javascript中数据访问性能优化简析
- 推荐:数据访问优化性能
- JavaScript性能优化-数据存取
- 《高性能Javascript》 学习笔记 web性能优化(二)
- JavaScript优化(二)
- 数据访问层的性能优化
- 性能优化_____MySql海量数据访问处理
- 【学习笔记】查询性能优化:优化数据访问
- Mysql性能优化(二) 索引优化
- Android性能优化(二)布局优化
- Javascript性能优化(一)
- Javascript性能优化(三)
- 高性能Javascript【二】数据访问
- mysql 性能优化(二)
- ABAP性能优化(二)
- js性能优化(二)
- SPOJ
- nodejs 调试
- java项目中如何判断是在liunx系统或者是在windows系统中
- 直接内存
- Java多线程JUC
- Javascript性能优化(二)- 数据访问优化
- springMVC 的工作原理和机制
- 按量付费实例批量更改实例带宽
- linux 不用./直接执行程序
- 七、输入/输出流--IOStreamm基本类和标准IOStream对象--未格式化输入和输出函数
- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
- Javascript学习之值传递和引用传递详解
- mysql
- 如何通过Docker 进行容器编排