Javascript提速_01_引用变量优化
时间:2010-10-12 来源:笨蛋的座右铭
关于执行环境(Execution Context)
所有 JavaScript 代码都是在一个执行环境中被执行的。它是一个概念,一种机制,用来完成JavaScript运行时作用域、生存期等方面的处理。
可执行的JavaScript代码分三种类型:
1.Global Code,即全局的、不在任何函数里面的代码,例如:一个js文件、嵌入在HTML页面中的js代码等。
2. Eval Code,即使用eval()函数动态执行的JS代码。
3. Function Code,即用户自定义函数中的函数体JS代码。
不同类型的JavaScript代码具有不同的Execution Context
载入JS代码时创建一个全局执行环境,当调用一个 JavaScript 函数时,该函数就会进入相应的执行环境。如果又调用了另外一个函数(或者递归地调用同一个函数),则又会创建一个新的执行环境,并且在函数调用期间执行过程都处于该环境中。当调用的函数返回后,执行过程会返回原始执行环境。因而,运行中的 JavaScript 代码就构成了一个执行环境栈。
让我们来看一个示例:
<script type="text/javascript"> function Fn1(){ function Fn2(){ alert(document.body.tagName);//BODY //other code... } Fn2(); } Fn1(); //code here </script>
以上是程序从上到下执行时的执行环境栈情况图。
补充说明:
全局执行环境对应的是Global Code(全局代码)
Fn1执行环境、Fn2执行环境通称为函数执行环境对应的是Function Code(函数定义代码)
程序在进入每个执行环境的时候都会创建一个叫做Variable Object的对象。
针对于函数执行环境,函数对应的每一个参数、局部变量、内部方法都会在Variable Object上创建一个属性,属性名为变量名,属性值为变量值。针对于全局执行环境,具有相同的行为。但是要强调的一点是在全局执行环境中Varible Object就是Global Object,关于Global Object在《理解Javascript_03_javascript全局观》中已经说明了,可以简单的理解为window对象。这也就解释了全局方法和全局变量为什么都是window对象的属性或方法的原因,请看如下代码:
var num = 123; alert(window.num);//123 function say(msg){ alert(msg); } window.say("hello");//hello
最后要说的是,Variable Object对象是一个内部对象,JS代码中无法直接访问。
关于Scope/Scope Chain
在访问变量时,就必须存在一个可见性的问题,这就是Scope。更深入的说,当访问一个变量或调用一个函数时,JavaScript引擎将不同执行位置上的Variable Object按照规则构建一个链表,在访问一个变量时,先在链表的第一个Variable Object上查找,如果没有找到则继续在第二个Variable Object上查找,直到搜索结束。这也就形成了Scope Chain的概念。
作用域链图,清楚的表达了执行环境与作用域的关系(一一对应的关系),作用域与作用域之间的关系(链表结构,由上至下的关系)。
引用变量优化
即然我们已经了解了Javascript中变量的访问规则,那我们就来具体分析一下上面代码中alert(document.body.tagName);这一句的具体执行流程:
由此可见,要在Fn2中访问window对象下的document对象的body属性的tagName属性是一个比较漫长的过程(这个问题在IE浏览器中尤为严重)。
那么如何优化呢,通过流程图,我们也能发现问题的原因在于引用的级数太多,在《理解Javascript_01_理解内存分配》一文中也说过,通过引用寻找堆中的实际对象也是要花费时间的,这就是性能的瓶颈。现在问题的关键就是如何减小引用的数目,来看一个最简单易行的方案:
function Fn1(){ function Fn2(document){ alert(document.body.tagName);//BODY //other code... } Fn2(window.document); } Fn1(); //code here
我们直接将document作为参数传递给Fn2,来看一下现在的执行流程:
现在方便了,从Fn2 Variable Object可以直接定位到document,引用减少了,效率提高了。
Jquery的策略
Jquery对引用变量的优化也下足了功夫,下面来看一下Jquery的相关代码:
(function(win, undefined) { var jQuery = function(selector, context) { return new jQuery.fn.init(selector, context); }, document = window.document, push = Array.prototype.push, slice = Array.prototype.slice; win.$ = win.jQuery = jQuery; })(window);
首先,Jquery定义了一个全局的匿名函数,这个函数有两个参数,一个为win,另一个为undefined.这里说明一点,undefined即不是一个关键字,也不是一个保留字,因此它作了参数的变量名是没有问题的。在调用这个匿名函数时,只传递了一个参数window对象,此时,win指向window对象,undefined变量指象window.undefined(深层次的原理,将在以后的文章中探讨)。然后在匿名函数中,Jquery对常用的全局对象都进行了引用,以后在匿名函数中就可以随意的访问这些变量了。
参考:
http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html