执行JavaScript代码主要分为以下两个阶段
-
代码预编译阶段
-
代码执行阶段
预编译阶段是前置阶段,这一阶段会由编译器将JavaScript代码编译成可执行的代码。跟传统的编译不同,传统的编译非常复杂,涉及各种分词、解析、代码生成等过程。这里的预编译是JavaScript中的独特概念,虽然JavaScript是解释型语言,编译一行,执行一行。但是在代码执行前,JavaScript引擎确实会做一些
''预先准备工作''
预编译过程中的一些细节注意以下3点
1、在预编译阶段进行变量声明。
2、在预编译阶段对变量声明进行提升,但是值为undefined。
3、在预编译阶段对所有非表达式的函数声明进行提升。执行阶段主要任务是执行代码逻辑,执行上下文在这个阶段会全部创建完成。
注意预编译过程中的3点会帮助我们正确理解和判断代码逻辑。看如下题目:
function bar(){
console.log('bar1')
}
var bar =function(){
console.log('bar2')
}
bar()
// 输出bar2
调换一下代码顺序
var bar =function(){
console.log('bar2')
}
function bar(){
console.log('bar1')
}
bar()
// 输出bar2
以上代码输出的结果都是bar2,因为在预编译阶段虽然对变量进行了声明和提升,但是不会对其进行赋值。函数bar被创建并且提升。但是在代码执行阶段的时候才会(通过表达式)赋值。
可以把上面代码理解为以下代码
function bar(){
console.log('bar1')
}
var bar = undefined;
bar = function () {
console.log('bar2')
}
bar()
这样就能理解为啥都是输出bar2了吧。
请再思考一下以下这道题
foo(10)
function foo(num){
console.log(foo)
foo=num
console.log(foo)
var foo
}
console.log(foo)
foo=1
console.log(foo)
根据上面说的JavaScript预编译的3个注意点我们可以将代码转换成如下
function foo(num){
var foo=undefined
console.log(foo)
foo=num
console.log(foo)
}
foo(10)
console.log(foo)
foo=1
console.log(foo)
这样就能知道以上输出分别是什么
undefined
10
foo函数本身
1
结语:
作用域在预编译阶段确定,但是作用域链是在执行上下文的创建阶段完全生成的,因为函数在调用时才会创建对应的执行上下文。执行上下文包裹变量对象、作用域链以及this的指向
代码执行的整个过程说起来就像一条生产流水线。第一道工序是在预编译阶段创建变量对象(Variable Object,VO),此时只是创建,未进行赋值。到了下一道工序代码执行阶段。变量对象会转为激活对象(Active Object,AO),即完成了VO向AO的转换。此时,作用域链也将被确定,它由当前执行环境的变量对象和所有外层已经完成的激活对象组成。这道工序保证了变量和函数的有序访问,即如果未在当前作用域找到变量,则会继续向上查找知道全局作用域。、
网友评论