JavaScript预解析
一、预解析
在理解 JS 的预解析前,我们先来看几个简单的代码程序,看看都会出现什么结果。
代码一:没有声明 num
变量,直接在控制台打印 num
变量。
<script>
console.log(num);
</script>
代码二:先在控制台打印 num
变量,之后在声明并赋值。
<script>
console.log(num);
var num = 10;
</script>
代码三:先调用函数,之后在用函数关键字 function
来声明和定义函数。
<script>
fn();
function fn(){
console.log('你好');
}
</script>
代码四:先调用函数,再用函数表达式的方法声明和定义函数。
<script>
fun();
var fun = function(){
console.log('大家好');
}
</script>
![代码执行结果](https://tva1.sinaimg.cn/large/e6c9d24ely1go6ob574rjj21gd0u0qep.jpg)
如果能很好的理解上述的四段小程序为什么会输出相应的结果,那应该就是懂了 JS 预解析的过程。不能理解也不要紧,看完下面介绍就能完全搞懂。
其实 JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。
-
预解析:JS 引擎会把 JS 代码里所有的
var
(变量预解析) 和function
(函数预解析) 提升到当前作用域的最前面。 -
代码执行: 预解析后再按照代码执行的顺序从上往下执行。
二、变量预解析与函数预解析
- 变量预解析:就是把所有的变量声明提升到当前作用域的最前面,不提升赋值操作。
<script>
console.log(num);
var num = 10;
// 上面代码相当于执行了以下代码
var num; // 变量提升 默认值:undefined
console.log(num);
num = 10;
</script>
以上是开头代码二的实际执行过程,先把 num
变量声明给提到了当前作用域前面,并没有提升赋值操作,所以默认就是 undefined
。下面是代码四的实际过程。这里就能明白代码二和代码四输出的结果啦!
<script>
fun();
var fun = function(){
console.log('大家好');
}
// 上面代码相当于执行了以下代码
var fun; // 变量提升
fun(); // 还不是函数
fun = function(){
console.log('大家好')
}
</script>
- 函数预解析:就是把所有的函数声明提升到当前作用域的最前面,不调用函数。
<script>
fn();
function fn(){
console.log('你好');
}
// 上面代码相当于执行了以下代码
function fn(){
console.log('你好');
}
fn();
</script>
这里要注意,函数提升提升的是用函数关键字 function
声明的函数,而像代码四中用函数表达式的方法声明不提升。
三、预解析案例分析
- 案例一:输出结果是什么?
<script>
var num = 10;
fun();
function fun(){
console.log(num);
var num = 20;
}
</script>
- 案例二:输出结果是什么?
<script>
var num = 10;
function fn(){
console.log(num);
var num = 20;
console.log(num);
}
fn();
</script>
- 案例三:输出结果是什么?
<script>
var a = 18;
f1();
function f1(){
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}
</script>
- 案例四:输出结果是什么?
<script>
f1();
console.log(c);
console.log(b);
console.log(a);
function f1(){
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
</script>
有想到每个案例的结果吗?这里给出每个案例的结果,简单提下案例四里第 7 行代码 var a = b = c = 9;
相当于 var a = 9; b = 9; c = 9;
b
和 c
变量前面都没有关键字 var
所以他们是全局变量,函数外面也能够访问到。
案例一:undefined
案例二:undefined 20
案例三:undefined 9
案例四:9 9 9 9 9 报错
下面提供每个案例代码的实际流程代码。
// 案例一原代码
<script>
var num = 10;
fun();
function fun(){
console.log(num);
var num = 20;
}
</script>
// 相当于以下代码
<script>
var num; // 变量提升 默认值:undefined
function fun(){ // 函数提升
var num; // 函数内部变量提升 默认值:undefined
console.log(num); // 打印就近的变量 undefined
num = 20;
}
num = 10;
fun();
</script>
// 案例二原代码
<script>
var num = 10;
function fn(){
console.log(num);
var num = 20;
console.log(num);
}
fn();
</script>
// 相当于以下代码
<script>
var num; // 变量提升 默认值:undefined
function fn(){ // 函数提升
var num; // 函数内部变量提升 默认值:undefined
console.log(num); // 打印就近的局部变量 undefined
num = 20; // 给函数内部变量赋值20
console.log(num); // 打印就近的局部变量 20
}
num = 10
fn();
</script>
// 案例三原代码
<script>
var a = 18;
f1();
function f1(){
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}
</script>
// 相当于以下代码
<script>
var a; // 变量提升 默认值:undefined
function f1(){ // 函数提升
var b; // 函数内部变量b提升 默认值:undefined
var a; // 函数内部变量a提升 默认值:undefined
b = 9; // 给函数内部变量b赋值9
console.log(a); // 打印就近的局部变量a undefined
console.log(b); // 打印就近的局部变量b 9
a = '123'; // 给函数内部变量a赋值'123'
}
a = 18;
f1();
</script>
// 案例四原代码
<script>
f1();
console.log(c);
console.log(b);
console.log(a);
function f1(){
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
</script>
// 相当于以下代码
<script>
function f1(){ // 函数提升
var a; // 函数内部变量a提升 默认值:undefined
a = 9; // 给函数内部变量a赋值9
b = 9; // 定义全局变量b并赋值9,不是 var 关键字修饰不提升
c = 9; // 定义全局变量c并赋值9,不是 var 关键字修饰不提升
console.log(a); // 打印就近的局部变量a 9
console.log(b); // 打印全局变量b 9
console.log(c); // 打印全局变量c 9
}
f1();
console.log(c); // 函数外打印全局变量c 9
console.log(b); // 函数外打印全局变量b 9
console.log(a); // a 是函数内局部变量,函数外不能访问,报错
</script>