每一段javascript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或链表,这组对象定义了这段代码“作用域中“的变量。
在javascript的最顶层代码中(也就是不包含在任何函数定义内的代码),作用域链由一个全局对象组成。在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对旬,第二个是全局对象。在一个嵌套的函数体内,作用域链上至少有三个对象。
当javascript需要查找变量x的值时,它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,javacript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,依次类推。如果作用链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x ,并最终抛出一个引用错误异常。
下面是一段代码:
<script type="text/javascript"> name="haha"; function t(){ var name="haha_t"; function s(){ var name="haha_s"; console.log(name); } function ss(){ console.log(name); } s(); ss(); } t(); </script>
当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是"haha_s"。
但执行ss()时,作用域链是: ss()->t()->window,所以name是”haha_t"
对象链的创建规则:
当定义一个函数fn时,实际上保存了一个作用域链scope_chain。当调用fn时,创建了一个新的对象obj来存储fn的局部变量,并将对象obj添加到scope_chain上,同时创建一个新的更长的表示函数调用作用域的”链“。对于嵌套函数来讲,每次调用外部函数时,作用域链都会不同,内部函数都会重新定义一遍。也就是说,在每次调用外部函数时,内部函数的代码都相同,而关联这段代码的作用域链不相同。
另外,我们要注意以下几点:
1、访问局部变量比全局变量速度快。
2、因为局部变量在作用域链的顶端,在函数中将频繁使用的全局变量赋值给局部变量,可提高程序执行的效率。
3、With语句改变作用域链,是将一个对象放入作用域链的最顶端,这样访问with语句的对象很快,但是访问局部变量就慢了一个级了。不推荐使用with语句,因为可以将对象赋值给局部变量,访问速度也很快,节省了改变作用域链的消耗。
4、With、eval、catch子句,他们都可以改变作用域链,令到一些对访问变量进行优化的引擎没法正常优化 ,因为可以将对象赋值给局部变量,访问速度也很快,节省了改变作用域链的消耗。