layout | title | |
---|---|---|
default |
|
一般数据的存储位置是很影响性能的,但是JS的问题相对简单,因为可选方案比较少。但是存储位置很大程度影响读取速度。
- 字面量:只代表本身,不存储在特定位置,比如字符串,数字,布尔,对象,数组,函数,正则等,null,undefined。
- 本地变量:使用var定义的存储单元
- 数组元素:存在数组对象内部,数字作为索引
- 对象成员:存在js对象内部,字符串作为索引
一般字面量和本地变量的存取性能基本可以忽略,数组元素和对象成员的稍微高一些。具体的实现也是看浏览器的,比如FF对于数组的性能进行了优化,也会很快,但是一般对象成员的损耗是最高的。
作用域很重要,不只是性能,还有功能的角度。
JS函数是一个对象,是Function对象的实例。
与其他对象一样,既拥有可以编程访问的属性,还有一系列仅供JS引擎存取的内部属性
,比如[[scope]]。
[[scope]]包含了一个函数被创建的作用域中对象的集合,这个集合被称之为函数的作用域链。如果是一个全局对象,那么作用域链就会插入一个指向全局对象的一个指针,可以访问到全局中的所有对象。
执行函数时会创建一个执行环境,每次执行函数都会创建执行环境。
每个执行环境都有自己的作用域链,当执行时,他的作用域链初始化为[[scope]]的作用域链,然后创建一个包含了所有局部变量(包括参数,this)的"活动对象",添加到作用域链的最顶层。当执行完成,执行环境被销毁,活动对象也随之销毁。
每次进行变量的查找时,都会从执行环境作用域链的顶部(活动对象)开始查找,正是这个搜索的过程影响了性能。
很明显,变量埋得越深,查找越需要消耗性能。
解决方法也比较简单,就是跨作用域的值如果访问了多次的话,就用一个局部变量存储起来
。
with语句 与 try--catch语句 with是可以延长作用域链的,就是说使用了with之后,这个对象的所有属性放入一个变量对象,然后放置到了作用域链的顶部。
try...catch语句的catch也是可以延长作用域链,异常对象会被放入变量对象,然后放到作用域的顶部
注意这里是临时被改变的,catch或者with语句一旦执行结束,作用域链就会变回原来的状态。
我们可以把catch语句里面的错误委托给一个函数来处理。由于只执行了一条语句,而且没有局部变量访问,作用域的临时改变就不会影响代码性能。
with,try--catch,和eval都是动态的作用域,有些浏览器的优化是没法支持动态作用域的。
闭包是外部函数被执行的时候创建的。
外部函数被执行时,他会创建自己的活动对象,然后加到自己的作用域链中。闭包会被创建,作用域链初始化为外部函数的活动对象以及全局对象。然后闭包被执行的时候,又会往自己的作用域链中推入自己的活动对象。
一般情况下,函数执行完成,执行环境与活动对象就会被销毁,这里由于闭包仍然拥有外部函数活动对象的引用,所以暂时无法回收。注意IE由于使用非原生Js对象来实现DOM,因此会导致内存泄漏(勐喆说IE不能搞定循环依赖)(说是DOM对象被回收了,JS对象可能没法回收,导致内存的泄露)
比如:
function a(){
function b(){
}
return b;
}
//到这里啥都不执行,a的作用域链为全局,b还未被创建
var c = a();//执行过程中a的作用域链为a的活动对象+全局,c即为b,作用域链为a的活动对象+全局。执行完a的执行环境销毁,但是a的活动对象还存在,因为c引用着。
c();//这样子执行,c的作用域链为c的活动对象+a的活动对象+全局;执行完了,执行环境被销毁,c的作用域链为a的活动对象+全局,所以a的活动对象就不会被回收
//除非脚本执行完了,或者手动设置为null,或者简单地用个自定义匿名函数包起来。
a()();//这样子执行,执行完了所有的活动对象,作用域就被销毁了
所以闭包的内存问题其实是由于不正确的使用导致的。但是我们用个匿名函数包起来也能够结果,就怕那种循环嵌套的,会导致内存可能会泄露。
原型链的访问同理,需要遍历原型链,所以埋得越深当然性能会消耗的越多
嵌套成员一样,window.location.href肯定是比location.href慢的
就是尽量避免读取同一个对象属性,用局部变量来保存一下。