方法链、作用域链和原型链(二)——作用域链

每一段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子句,他们都可以改变作用域链,令到一些对访问变量进行优化的引擎没法正常优化 ,因为可以将对象赋值给局部变量,访问速度也很快,节省了改变作用域链的消耗。

原文地址:https://www.cnblogs.com/aaron-shu/p/4185320.html