欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Javascript 解析过程及其作用域链

程序员文章站 2022-07-13 14:51:27
...

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(){}
但是相比varfunctionvar 提前在function的前面

console.log(a);
function a(){}
var a = 2;

如上,当varfunction的名字一样时,将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()函数体;
所以三次输出分别为undefined32

关于作用域链,就是一个作用域里面有子级作用域,当子级作用域里没有需要的值时会向父级作用域里借用,由此形成的一种链式结构称为作用域链。

<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。
函数执行完成后,元素出栈,活动对象和局部变量一同释放。
Javascript 解析过程及其作用域链

其中main()为父作用域,test()为子作用域,两者的关系如图橙色部分所示,形成的链式结构为作用域链。