for循環內外定義變量的區別

直接上代碼

			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存在變量申明提升

運行結果如下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章