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(){}
但是相比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。
函数执行完成后,元素出栈,活动对象和局部变量一同释放。
作用域链

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

发布了33 篇原创文章 · 获赞 24 · 访问量 5535

猜你喜欢

转载自blog.csdn.net/qq_39157944/article/details/103015561
今日推荐