直接上代码
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存在变量申明提升
运行结果如下