JS作用域的理解
理解JS函数的作用域有助于我们分析函数执行,调用。
什么是作用域?
JS的函数作用域,将作用域拆开来看,“作用”表示读写操作,函数可以读取代码,改写代码;“域”表示空间,范围,区域。一般指在
函数作用于的操作流程
函数在执行的时候,至少会发生这两件事。第一是预解析,第二是逐行解读代码。
预解析:函数在执行的时候,会读取到整个函数内的关键字或参数。比如var、function、参数。读取过程中并不会读取到等号右边的代码。也就是说当读取到var的时候,其实是一种未定义的状态,读取到function的时候,是将整个函数全部提取; 逐行解读代码:预解析之后,就来逐行解读代码,执行代码;当在逐行解读的时候,如果碰到函数调用,那么就会先解读函数内部的代码块,这个时候就又会遵循函数作用域的执行过程。先解析,再解读。其中就涉及到全局变量和局部变量的操作。 逐行解读代码的一些原则:
1、逐行解读过程中,当碰到表达式的时候,就会用表达式修改预解析的值;表达式是=、+、-、*、/、参数(注意,参数也是表达式,参数可以被修改)
2、预解析的时候,遇到重名的变量名称,字母,值留下一个;
3、预解析时,当变量碰到函数的名称一致的时候,值留下函数块。因为预解析的时候,变量其实是未定义的状态
4、逐行解读代码时,碰到函数,如果没有调用,表示函数没有执行。此时直接略过,直到有表达式才能改变变量的值。
5、所有变量,在正式运行代码之前,都会提前赋一个值;未定义。所有函数,在预解析的时候,都是函数块;
6、逐行解读代码时,是从上到下,从函数内部到函数外部的执行过程
简单案例:
<script type="text/javascript">
alert(a);
var a=1;//undefined
</script>
<script type="text/javascript">
var a=1;
alert(a);//弹出1
</script>
以上这两段代码,只是调换了一下顺序,执行的结果就不相同。分析一下它们的执行过程 第一段代码:1、解析器先找到
2、然后逐行解读代码,从上到下,先解析了alert的代码,并执行,这个时候a还是属于一种未定义的状态,因此弹出了undefined;
第二段代码:1、解析器先找到
2、逐行解读代码。从上到下,var a=1,为a进行了赋值,此时a=1,再解读alert(a)的值,那么最后弹出1.
复杂的案例:
alert(a);
var a = 1;
alert(a);
function a (){ alert(2); }
alert(a);
var a=3;
alert(a);
function a (){ alert(4); }
alert(a);
上述这行代码,比较复杂,但我们依然按照作用域的解析过程来分析分析
1、预解析。找到关键字var和function。 从上到下,依次预解析的值有变化。先是第二行代码,var a是一种未定义;然后是第四行代码,是一个函数,此时函数a的值就覆盖了第二行代码的未定义;第六行变量依旧是未定义,修改不了;第八行是一个函数,此时就将第四行的函数块修改了。最后预解析保存的是第八行代码:function a (){ alert(4); }
2、逐行解读。
2-1、预解析过程后保存的代码是函数块,a=function a(){alert(4)},此时执行第一行代码,alert(a),那么就会弹出第八行代码。function a(){alert(4)}
2-2、解读第二行代码,a=1,此时将函数代码块修改了,a=1,那么第三行代码则会弹出1;
2-3、解读到第四行代码,是一个函数,没有调用,也就没有表达式,直接跳过执行第五行代码,依然是a=1;
2-4、执行第六行代码,是一个表达式,将a修改为3;那么第七行代码就会弹出一个3
2-5、执行到第八行代码时,是一个函数块,依旧没有调用,直接跳过执行第九行代码,所有最后弹出依旧是3
所以最后的结果是:
alert(a); // function a (){ alert(4);}
var a = 1;
alert(a); // 1
function a (){ alert(2); }
alert(a); // 1
var a=3;
alert(a); // 3
function a (){ alert(4); }
alert(a); // 3
如果在最后再调用a()的话,会怎么样呢?答案是报错。因为最后执行的结果是一个a=3,是一个数字类型。用函数调用产生报错。 作用域的执行过程中,先找到整个作用域内的关键字或参数;然后再逐步解析。碰到调用的函数,我们根据执行顺序,再去解析被调用的函数块内部的代码。 当我们碰到函数作用域的时候,就有全局变量和局部变量之分,进而产生闭包和作用域链的知识。我们将会在后续陆续讨论。