一、先预处理后执行
在一个JavaScript文件或一个JavaScript代码块的内部,浏览器会先对代码进行预处理(编译),然后再执行。
预处理会跳过执行语句,只处理声明语句,同样也是按从上到下按顺序进行的。包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。 即使声明是在调用的下方进行的,但浏览器仍然先声明再调用(执行),这个现象叫做“提升”。所以,即便一个函数的声明在下方,在前面仍然可以正常执行这个函数。
注意1:对于声明并赋值的语句,例如 var a = 1,在预处理阶段会把这句话拆成两句:
var a;
a = 1;
也就是说,赋值或其他逻辑运算是在执行阶段进行的,在预处理阶段会被忽略。
注意2:(1)函数声明的提升优先于变量声明的提升;(2)重复的var声明会被忽略掉,但是重复的function声明会覆盖掉前面的声明。
在预处理阶段,声明的变量的初始值是undefined, 采用function声明的函数的初始内容就是函数体的内容。
二. 执行顺序
完成预处理之后,JavaScript代码会从上到下按顺序执行逻辑操作和函数的调用。
实例一:
首先我们来看两段简短的代码
var a=1;
function func(){
console.log(a);
//var a=2;
console.log(a);
}
func();
运行结果为:在这里打印出来的a都是1
var a=1;
function func(){
console.log(a);
var a=2;
console.log(a);
}
func();
运行结果:在这里第一个a打印出来的结果是undefined,第二个是2
按照C/C++第个段代码输出的结果因该是1和2,为什么第一个a没法输出1呢?
分析原因:
1、在js语言中,没有类似于c语言这样的块级作用域。
2、js作用域链变量访问规则:
(1)、当前作用域内存在要访问的变量时,则使用当前作用域中的变量。
(2)、当前作用域中不存在要访问的变量时,则会到上一层作用域中寻找,直到全局作用域。
3、执行顺序:
(1)代码的检查装载阶段(预编译阶段),此阶段进行变量和函数的声明,但是不对变量进行赋值,变量的默认值为undefined。
(2)代码的执行阶段,此阶段对变量进行赋值和函数的声明。
4、看上面的代码:第一个a输出undefined。原因:js作用域链的访问规则,当前作用域内存在要访问的变量a,所以使用当前作用域中的变量。再根据js代码的执行顺序,此时的a只是声明了而并未被赋值,默认为undefined,所以输出undefined。而第二个a,输出1,正是因为此时的a已经被声明且被赋值,所以a输出1。
在当前作用域内存在要访问的变量a,则就会使用当前作用域的变量a,只要当前作用域存在该变量即是对该变量进行了声明(即不会再用作用域外的值),直到var a=2;才是对该变量进行赋值。在代码中先是执行了console.log(a);在执行var a=2;所以此时a在该作用域内只是进行了声明还未进行赋值,所以就会输出undefined.
实例二:
例子1
var hello = function(){
console.log('hello,zhangsan');
}
hello();
var hello = function(){
console.log('hello,lisi');
}
hello();
运行结果:
例子2
function hello(){
console.log('hello,zhangsan');
}
hello();
function hello(){
console.log('hello,lisi');
}
hello();
运行结果:
JavaScript执行引擎并非一行一行地分析和执行程序,而是一段一段地分析执行的。而且在分析执行同一段代码中,定义式的函数语句会被提取出来优先执行。函数定义执行完后,才会按顺序执行其他代码。
问题:在例子2中,两次调用都会输出相同的内容“hello,lisi”。同样是声明两个相同名称的函数,为什么调用的结果却不一样呢?
这就是JavaScript执行顺序导致的。JavaScript执行引擎并非一行一行地分析和执行程序,而是一段一段地分析执行的。而且在分析执行同一段 代码中,定义式的函数语句会被提取出来优先执行。函数定义执行完后,才会按顺序执行其他代码。也就是说,在第一次调用hello函数之前,第一个函数语句 定义的代码已经被第二个函数定义语句的代码覆盖了,这就是为什么在例子2中第一次调用hallo时,也会输出后面定义的函数内容的原因了。