for循环内外定义变量的区别

本文主要分析了JS中变量的作用域、内存空间和申明提升问题。对于引用型和基本类型变量,在for循环内外申明赋值有不同表现,如外层申明赋值会覆盖,内层则不会。还指出JS用var定义的变量无块级作用域,存在变量申明提升现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接上代码

			var attrOuter:Object={};
			var objOuter:Object={};
			var indexOuter:int;
			var objInner:Object={};
			for (var i:int=0; i < 5; i++)
			{
				indexOuter=i;
				attrOuter.name=i.toString();
				attrOuter.value=i * 100;
				attrOuter.index=indexOuter;
				objOuter[i]=attrOuter;
			}
			for (var j:int=0; j < 5; j++)
			{
				var attrInner:Object={};
				var indexInner:int=j;
				attrInner.name=j.toString();
				attrInner.value=j * 100;
				attrInner.index=indexInner;
				objInner[j]=attrInner;
			}
			for (var keyOuter:int in objOuter)
			{
				console.log("objOuter" + keyOuter + JSON.stringify(objOuter[keyOuter]));
			}
			console.log("--------------");
			for (var keyInner:int in objInner)
			{
				console.log("objInner" + keyInner + JSON.stringify(objInner[keyInner]));
			}

运行出来的结果如下

从运行结果不难发现,对于引用型变量,如果变量申明在for循环外层,那么for循环内多次对变量进行赋值时,相同的属性,后面的赋值会覆盖前面的赋值。如果变量申明在for循环内层,for循环内多次对变量进行赋值时,相同的属性,后面的赋值不会覆盖前面的赋值。从计算机内存来分析,每次申明变量,就会为变量开辟一个内存空间(基本类型存储在栈中开辟内存空间,用完即出栈,引用类型在堆中开辟内存空间),对于在for循环外层申明的变量,for循环内多次对变量进行赋值时,操作的是同一处内存空间,同一个变量。对于在for循环内层申明的变量,for循环内每次在对变量进行赋值时,都会为变量开辟新的内存空间,即使这些变量在调用时的变量名一样,但是for循环每循环一次,之前申明的变量就已经出栈了(JS没有块级作用域,所以直到变量所在所在方法执行完毕之后,局部变量才会出栈),所以其实不存在说多个变量名相同的变量存储在内存中不同区域,变量名实际上用完即出栈。

以上分析的是引用型变量,但是index变量为基本类型变量,在此测试中,仍然符合引用型变量的规则,for循环外部申明时,后面的赋值覆盖前面的赋值,for循环内部申明index变量时,后面的赋值不会覆盖之前的赋值。原因是基本类型也分成员变量和局部变量,局部变量随着方法入栈就存在栈中,而成员变量会随着对象在堆中空间的开辟而存储在堆中。

下面再测试纯粹基本类型在for循环内外定义的区别

for循环内部定义基本变量类型,每次执行for循环都重新申明变量,不覆盖之前的值,对于for循环外部定义的基本类型变量,代码如下

	var indexOuter:int=-1;
			var recordIndexOuter:int=indexOuter;
			for (var i:int=0; i < 5; i++)
			{
				indexOuter=i;
				console.log("recordIndexOuter=" + recordIndexOuter);
			}

运行结果如下

输出了5次-1,说明基本变量类型在for循环外部被赋值为基本变量类型indexOuter的值后,指向了内存值为indexOuter当前指向的内存空间的值,当给indexOuter重新赋值时,相当于修改indexOuter的指针,但是基本类型变量recordIndexOuter的指针并未被修改。

 

这里还一个有意思的现象,如果方法内存存在多个{}

			if (true)
			{
				//对变量a进行申明
				var a:int=1;
				console.log("a=" + a);
			}
			//变量a是在{}内部申明和赋值的,但是这里能拿到a初始化之后的值
			console.log("a=" + a);
			if (true)
			{
				//	这个{}内没有对变量a进行申明,但是可以直接对变量a进行赋值,不会报错
				console.log("a=" + a);
				a=2;
				console.log("a=" + a);
			}

运行结果

这与预期不一致,预期的第二行和第三行应该打印未定义。这是因为JS用var定义的变量没有块级作用域(ES6推出的let也是用来生命变量的,但是let申明的变量存在块级作用域)。在第一次对变量a进行初始化赋值为1之后,在对a重新赋值之前,整个方法内部都可以访问到a的值为1。

下面对JS块级作用域进行测试

			//这时候只进行了JS的变量申明提升,还没有对a进行初始化赋值,所以打印未定义
			console.log("a=" + a);
			if (true)
			{
				//对变量a进行申明
				var a:int=1;
				console.log("a=" + a);
			}
			//变量a是在{}内部申明和赋值的,但是这里能拿到a初始化之后的值
			console.log("a=" + a);
			if (true)
			{
				//	这个{}内没有对变量a进行申明,但是可以直接对变量a进行赋值,不会报错
				console.log("a=" + a);
				a=2;
				console.log("a=" + a);
			}

可以发现,在对变量a进行申明之前,就对a初始化为1,而没有报错,这是因为JS存在变量申明提升

运行结果如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值