// 面试题
var a = 1
function fn() {
console.log(a)
a = 3
console.log(a)
var a = 2
console.log(a)
function a() {}
console.log(a)
}
console.log(a)
fn()
console.log(a)
话不多说,先上结果
1
ƒ a() {}
3
2
2
1
JS编译过程
看到上面的结果,看官您不用慌,这是涉及到JS编译原理的部分,看完后面的内容,相信类似的题目对您来说也不是难事了。
JS代码的执行是分两个部分的,第一部分是编译,第二部分是执行,顺序是先编译后执行。那么编译阶段和执行阶段又各自做了哪些事情呢?
编译阶段
在编译阶段,会对整个JS代码进行排查,寻找var/function等定义变量的关键字,但是赋值语句并不执行,后面会详细介绍。function关键字会把变量赋值为函数类型,并直接将函数赋值给变量
执行阶段
执行阶段就是执行编译阶段后的JS代码,包括函数调用、赋值语句等等
简单了解了JS的编译过程,下面就通过讲解详细看看到底怎么执行的吧!
题目解答
按照JS执行顺序,先看编译阶段吧,在这里我会对变量的值进行一步步的记载,大家在做此类型的题目时,也可以像我一样。
编译阶段 - 1、var a = 1
这个语句可以拆分为两个部分:var a 和 a = 1,也就是一步是定义一步是赋值语句,之前提到过定义变量执行,而赋值不执行,所以这里只是执行了 var a,所以在编译阶段,此时的a的值是null。
变量: a = null
编译阶段 - 2、function fn() { /.../ }
编译阶段的function 会将fn变量赋值为函数,所以fn的值为function fn(){/.../},而根据JS特性已知的是函数的在赋值的时候,内部的内容是不会编译执行,只有当调用的时候才会编译执行。
变量: a = null
变量: fn = function fn() {/.../}
编译阶段 - 3、console.log(a)
这是一个执行语句,在编译阶段不会执行。
变量: a = null
变量: fn = function fn() {/.../}
编译阶段 - 4、fn()
这是一个执行语句,在编译阶段不会执行。
变量: a = null
变量: fn = function fn() {/.../}
编译阶段 - 5、console.log(a)
这是一个执行语句,在编译阶段不会执行。代码编译阶段结束,下面开始执行阶段
变量: a = null
变量: fn = function fn() {/.../}
执行阶段 - 1、var a = 1
在编译阶段已经执行了var a,在执行阶段就只要执行赋值语句就可以了,所以将1赋值给a
变量: a = 1
变量: fn = function fn() {/.../}
执行阶段 - 2、function fn() { /.../ }
在编译阶段已经执行完成了,执行阶段没有要执行的内容了
变量: a = 1
变量: fn = function fn() {/.../}
执行阶段 - 3、console.log(a)
console.log执行,在控制台上输出变量a的值,此时查看记录知道a的值是1,所以控制台输出1
变量: a = 1
变量: fn = function fn() {/.../}
执行阶段 - 4、fn()
执行函数调用,接下来将离开主程序的执行阶段,进入到函数fn内部的编译执行阶段。
变量: a = 1
变量: fn = function fn() {/.../}
fn 编译阶段 - 4.1、console.log(a)
编译阶段console.log不执行
变量: a = 1
变量: fn = function fn() {/.../}
fn 编译阶段 - 4.2、a = 3
编译阶段赋值语句不执行
变量: a = 1
变量: fn = function fn() {/.../}
fn 编译阶段 - 4.3、console.log(a)
编译阶段console.log不执行
变量: a = 1
变量: fn = function fn() {/.../}
fn 编译阶段 - 4.4、var a = 2
执行var a,但是a=2这个赋值语句不执行,由于此时是在函数内部声明的变量,所以变量将存在于函数内部,虽然和全局变量同一个变量名,但是由于作用域不同,所以并不冲突
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = null
fn 编译阶段 - 4.5、console.log(a)
编译阶段console.log不执行
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = null
fn 编译阶段 - 4.6、function a() {}
function定义函数执行,并将变量赋值为函数,赋值会先检查变量集合中有没有同名的变量,先检查局部变量,局部没有再检查全局变量,这里局部变量存在变量a,所以将a的值赋值为函数。
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = function a() {}
fn 编译阶段 - 4.7、console.log(a)
编译阶段console.log不执行,编译阶段执行完成,接下来fn的执行阶段
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = function a() {}
fn 执行阶段 - 4.1、console.log(a)
console.log执行,先检查局部变量,局部没有检查全局,这里局部存在a,所以控制台上输出 function a() {}
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = function a() {}
fn 执行阶段 - 4.2、a = 3
赋值语句执行,将局部变量的执行改为3
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 3
fn 执行阶段 - 4.3、console.log(a)
console.log执行,控制台上输出 3
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 3
fn 执行阶段 - 4.4、var a = 2
只有赋值语句执行,将局部变量的执行改为2
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 2
fn 执行阶段 - 4.5、console.log(a)
console.log执行,控制台上输出 2
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 2
fn 执行阶段 - 4.6、function a() {}
function编译阶段已经执行过,执行阶段不再执行
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 2
fn 执行阶段 - 4.7、console.log(a)
console.log执行,控制台上输出 2。fn的执行阶段执行完成,接下来继续执行主程序的执行阶段
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 2
执行阶段 - 3、console.log(a)
console.log执行,在控制台上输出变量a的值,注意这里是在全局执行的程序,js程序是不会向局部区域主动访问的,所以这里控制台输出是全局变量a,值为1
变量: a = 1
变量: fn = function fn() {/.../}
fn内部变量: a = 2
总结
至此,JS的编译执行阶段都已经模拟完成了,各位看官以后面试的时候遇到类似的问题时,可以按照我的方法一步步的来,基本是不会出错的。求赞、求赞、求赞!!!