Javascript 解析过程及其作用域链
Javascript在执行的步骤先通篇检查错误的语法,假设出现语法错误将无法执行脚本。
语法无错误的情况下开始预编译,申明变量提前,值为undefined,函数体整体提前。
预编译完成后将开始从上至下的原则进行解释执行,自动忽略变量声明和函数体的创建; 当变量与函数体名称重复时,后者覆盖前者,前者被回收。
其中预编译的过程,将var
声明的变量提前,Javascript
自动给值undefined
,赋值留在原地;function
声明的函数整体提前。
var 变量提前
<script>
console.log(a);
var a = 2;
console.log(a);
</script>
如上代码,var 变量声明提前,则等效于
<script>
var a;
console.log(a);
a = 2;
console.log(a);
</script>
所以第一次输出a
的值为undefined
,当代码执行到a = 2
时,a
被赋值,则第二次输出a
的值为2
function 函数体提前
console.log(test);
function test(){}
console.log(test);
由于函数体为整体提前,所以等效于
function test(){}
console.log(test);
console.log(test);
所以两次输出均为 function test(){}
但是相比var
和function
,var
提前在function
的前面
console.log(a);
function a(){}
var a = 2;
如上,当var
和function
的名字一样时,将var
声明的变量提前在function
的前面,等效于
var a;
function a(){}
console.log(a);
a = 2;
首先var
申明的变量a
被提前,值为undefined
,然后function
函数体提前,后值覆盖前置,a
的值为function a(){}
,所以输出function a(){}
函数体的预编译
当Javascript需要执行函数是,此时函数将进行预编译,过程一致,提前的位置不同。
每次提前的位置为当前作用域的前面。
<script>
var a = 2;
function test(){
console.log(a);
var a = 3;
console.log(a);
}
test()
console.log(a);
</script>
当代码开始预编译时,首先提前var a;
和function test(){....}
,作用域为window对象;
当代码执行到test()
时,提前申明var a;
,作用域为test()
函数体;
所以三次输出分别为undefined
,3
,2
。
关于作用域链,就是一个作用域里面有子级作用域,当子级作用域里没有需要的值时会向父级作用域里借用,由此形成的一种链式结构称为作用域链。
<script>
var a = 2;
function test(){
console.log(a);
}
test()
</script>
在函数体test()
中,自己本身不存在变量a
,所以向自己的父级作用域里借用,所以输出结果为2
。
<script>
var a = 2;
function test(a){
console.log(a);
}
test(4)
</script>
开始执行之前,创建执行环境栈:临时保存正在执行的函数的执行环境。
预编译时,创建函数对象, [[scope]]记录函数的归属,同时为后期的作用域访问范围做准备。
调用函数时,ECS加入一个新的元素,记录函数的调用,同时创建体格活动对象AO,保存了本次函数调用得问局部变量,函数scope记录着parent。
函数执行完成后,元素出栈,活动对象和局部变量一同释放。
其中main()
为父作用域,test()
为子作用域,两者的关系如图橙色部分所示,形成的链式结构为作用域链。
上一篇: 第一章 HTML 基础学习
下一篇: 使用satrda实现即时通讯