在预解析原理(一)中我们简单介绍了一下JS的解析过程,这篇文章会对这个过程进行深入的分析。
在这个过程中首先需要明白三个概念:
1、全局作用域:也就是全局变量声明在函数之外的变量默认作用整个工程;
2、局部作用域:声明在函数体中的变量,并且只能在当前函数体内访问,如:function(){var a = 0;};
3、作用域链:JavaScript中所有的变量都是存在于某一个作用域中的,除了全局作用域, 每一个作用域都是存在于某个作用域中的,在试图访问一个变量时JS引擎会从当前作用域开始向上查找直到找到全局作用域时停止,这个执行过程的有序集合就是作用域链;
明白了这三个概念我们就开始学习下边的内容:
1、全局作用域:
首先看一个例子
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
alert(a);
</script>
<script>
var a = 1;
</script>
</head>
</html>
在这个例子中有两个script,这两个script均是全局的作用域,但是当把alert(a);与var a = 1;分别写在两个不同的域中,就会出现一些无法想象的问题,例如:报错。
在这个过程中浏览器提示a没有被定义,是因为JS在执行的过程中是自上而下进行的,它在第一个作用域中没有找到a导致报错,然后程序终止了。
但是如果把上下顺序进行对调就会产生不一样的效果:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
</script>
<script>
alert(a);
</script>
</head>
</html>
这是因为,当解读到第一个域的时候,a作为变量被保留在“仓库”中了,然后自上而下执行到第二个域的时候就可以正常执行;
所以,当要引入一些JS文件的时候尽量写在上边,否则会出现意想不到的情况(JQ等进行处理的不算在内)。
2、局部作用域
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
function fn1 () {
alert(a);
var a = 2;
}
fn1();
alert(a);
</script>
</head>
</html>
首先不管是全局作用域还是局部的作用域在执行的时候都会进行前文中讲的那两部:1、JS预解析;2、逐行解读代码
所以以上的代码执行原理为:
var a = 1; 预解析找到var a被赋值为未定义;
function fn1 (){ alert(a); var a = 2;} 由于这是一个函数所有预解析的时候直接存为函数本身,也就是function fn1 (){ alert(a); var a = 2;} ;
然后由于没有其他的关键字以及函数,所以预解析完成执行下一步,
1、当程序独到var a = 1;的时候,“仓库”里边的a = 未定义,就会被替换成 a = 1;
2、执行function fn1 (){ alert(a); var a = 2;}的时候由于它既不是表达式又没有调用所有没有任何变化;
3、当执行到fn1();的时候,发生了函数调用,同时也触发了局部作用域:
1、执行JS预解析:(局部作用域的解析范围仅限于局部作用域内,所以他的解析行为如下)
找到var关键字,把a储存为未定义,由于函数内没有其他的内容了所以预解析结束,
预解析的结果:a = undefined ;
2、逐行解读代码:
局部作用域在进行这一步的时候会优先从内部进行查找,由于预解析的时候函数内是有a = 未定义的,然后会弹出 undefined,(这里有一个需要注意的地方函数内的 a 与 函数外的a没有任何联系。)最后执行var a = 2;的时候出现了表达式,则第一步的预解析结果就会由a = undefined变为a = 2;然后第二步执行完毕。
4、执行alert(a);由于这段代码是在函数外边,所以会调用全局变量var a = 1,最终弹出的结果就是1;
3、作用域链
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
function fn1 () {
alert(a);
a = 2;
}
fn1();
alert(a);
</script>
</head>
</html>
全局作用域与局部作用域的原理在此就不在进行解释了,如果有不明白的地方可以参考上边的内容。
当局部作用域在解析的时候由于函数内没有内容,这个时候就会触发作用域链,返回父级进行查找,所以当代码执行到alert(a);时会弹出 1 ;
然后逐行解读到a = 2;的时候函数内的变量会把全局变量修改为2,最后在执行alert(a);时,会弹出2。
那么加入参数之后会怎么样呢?
测试一下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<script>
var a = 1;
function fn1 (a) {
alert(a);
a = 2;
}
fn1();
alert(a);
</script>
</head>
</html>
当函数内进行预解析的时候由于函数有参数a,所以预解析的结果会变成undefined,执行函数内的alert(a);会弹出undefined,
在执行a = 2时,把2赋值给a,所以此时的局部变量有undefined变成了2;然后局部代码执行完毕。
当执行到全局的alert时,由于此时并不在函数内所以会弹出1.