javascript按照数据存储位置的不同可分为以下四类:
- 直接量(字符串、数字、布尔型、Object、Array、Function、正则表达式、NULL以及undefined)。
- 变量。
- 数组项。
- 对象成员。
javascript对以上四个类型数据的访问速度依次减慢,也就是说,访问直接量的速度是最快的,而访问对象成员的消耗的时间是最多的。
管理作用域
理解作用域是理解javascript
的关键。作用域对javascript
有非常多的影响,从知道哪些变量可以被访问,到确定this
的值。javascript
作用域也关系到性能。
作用域链
每一个javascript函数都可以看作一个对象,进一步说,它是一个函数实例。函数对象和其他的对象一样,拥有可编程访问属性和内部属性,其中[[Scope]]
就一个典型的内部属性。
内部属性[[Scope]]
包含一个函数被创建的作用域中的对象的集合,此集合被称为函数的作用域链。他决定那些数据可以用函数访问。此函数中的每个对象被称为可变对象。当一个函数被创建后,它的作用域链被填充以对象,这些对象代表创建此函数的环境可以访问的数据。例如下面这个全局函数:
function add(a,b) {
var sum = a + b;
return sum;
}
当add()
函数被创建后,它的作用域链被填入一个单独的可变对象,此全局对象代表所有全局范围内定义点变量。当add()
函数运行时,会创建一个“运行期上下文”。一个运行期上下文定义了一个函数运行时的环境,所以多次调用同一个函数会产生多个运行期上下文。当函数执行完毕,运行期上下文就会被销毁。
一个运行期上下文有它自己的作用域链,用于标识符解析。当运行期上下文被创建时,作用域链为初始化,连同运行函数的[[Scope]]
属性中所包含的对象。这些值按照它们在函数中出现的顺序被复制到运行期上下文所在的作用域链中。这项工作一旦完成,一个称为“激活对象”的新对象就为运行期上下文准备好了。此激活对象是一个可变对象,包含访问所有局部变量、命名参数、参数集合,和this
接口。然后此对象推入作用域链的前端。当作用域链销毁时,激活对象一同被销毁。下图展示了实例代码对应的运行期上下文:
在函数运行过程当中,每遇到一个变量,函数就从这个作用域链中依次进行查找,正是这种查找过程影响了性能。一个标识符所处的位置越深,读写它的速度就越慢。所以,当一个函数中,如果要频繁的调用一个全局变量,那么最好用一个局部变量把它保存下来。
闭包
闭包是javascript最强大的一面,它允许函数访问局部范围之外的数据。为了了解与闭包有关的性能问题,我们看下面的例子:
function assignEvents(){
var id = "123";
document.getElementById("savaBtn").onclick = function(e) {
savaDocument(id);
};
}
assignEvents()
函数为一个DOM元素指定了一个时间处理函数。此事件处理函数就是一个闭包,在assignEvents()
执行时创建,这个闭包可以访问其范围内部的id
变量。下图展示了函数运行期上下文的作用域和闭包:
由于闭包的[[Scope]]
属性包含与运行期上下文作用域链相同的对象引用,会产生副作用。通常,一个函数的激活对象与运行期上下文一同销毁。当涉及闭包时,激活对象就无法销毁了。这意味着,闭包与非闭包函数相比,需要消耗更多的内存。
当一个闭包被执行时,它的作用域链与[[Scope]]
中引用的两个相同的作用域链同时被初始化,然后一个新的激活对象为闭包自身所创建。如下图所示:
注意闭包中使用的两个标识符:id
和saveDocument
,他们并不是在作用域链中的最前端,这就造成了数据读写的性能问题。
原型
javascript中的对象是基于原型的,原型是其他对想的基础,定义一个新的对象必须具有的成员。对象成员分为实例成员和原型成员,你可以通过hasOwnProperty()
函数来确定某个成员是否为实例成员。要确定某个对象是否有某个属性,可以用in
关键字来确定。
原型链
可以通过prototype
属性来构造原型链,但是要注意的是:对象在查找成员的过程就像函数作用域链查找变量的过程类似,先查找实例成员,如果找不到再从原型成员中查找,所以也有类似的性能问题。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。