众所周知,JS里的数组对象为引用数据类型,通过传递地址来起到调用数组的作用;然而,其具体执行过程笔者一直都是云里雾里,稀里糊涂,近日研究了不少资料,特此总结分享。
开始前,先上一组代码,来体现基本数据类型的调用:
var a = 10;
function show(a){
a = 5;
console.log(a);
}
对于变量a,如果把它分三个阶段输出,结果如下:
console.log(a);//10
show(a);//5
console.log(a);//10
这个结果完全在意料之中:JS函数的作用域置己身外,所以声明在当前函数中的变量,以及当前函数的形参,其被限制在该函数内。
当调用函数时,形参先从外界获取相应变量值(此例中为“a = 10”),而在本函数内重新对该变量赋值后,按照就近原则,变量被赋予函数内给予的值(此例中为“a = 5”),并对其进行输出打印。
而在函数调用结束以后,其作用域连带着所有的内部变量与形参一同被摧毁,再次访问变量时,即获得最开始定义的值。
以图解形式表达如下:
(1)当调用show()函数时,通过形参获取到a = 10;
(2)函数内部给a赋值为5,覆盖了先前的值;并进行输出;
(3)函数调用完毕,作用域被销毁,再次访问a时直接获取a = 10;
嘛,以上的是基本数据类型的调用过程;言归正传,作为引用类型的数组,是怎么被调用的呢?
先来看一组代码:
var arr = [10,20];
function show(arr){
arr[2] = 30;
arr[3] = 40;
console.log(arr);
}
console.log(arr);//(2) [10, 20]
show(arr); //(4) [10, 20, 30, 40]
console.log(arr);//(4) [10, 20, 30, 40]
调用函数时改变了数组的值,我们会发现即便函数调用结束,数组的值仍保持修改后的样子;我们都知道这是所谓的“引用”带来的结果,而它到底具体是怎么运行的呢?
这里就需要引出一些概念:在JS中,函数/对象运行的内存空间是预先分配好的,一旦被分配好空间,就不能再进行改动;而像本例一样初始化一个数组时,它的大小可以随意改动–具体需要多少内存空间是未知的,所以极易造成为内存分配不足的情况,为了解决这个问题,引出了一个被视为内存空间的分区,名为“堆段”;
在堆段中,内存的分配更为自由,想要使用多少空间就可以随时分配多少空间。所以 系统会将所有的符合引用数据类型都不直接存储在我们的函数/对象中,而是存储在堆段中。例如数组arr,它所初始化的值都被存放在了堆里,而arr本身将存放一个类似地址的值——可以帮忙从堆段里找到原本属于自己的数据。
而此时,整个代码块传递的也不再是具体的值,而是值所在的地址,因而当函数对引用数据类型变量进行增删改时,实际上影响的是存放在堆中的数据本身,当函数调用结束,作用域被摧毁,堆中的数据已经被完完全全改变了。再次调用该变量,自然得到的是经过函数改动后的值。
空口说来确实繁琐,见下列图解:
(1)创建并初始化一个数组,其值存放于堆中,而堆将给arr提供一个地址以便访问:
(2)调用show()函数时,地址被作为形参传输到函数中:
(3)当函数内部对数组的值进行改动时,实则是通过传输进来的地址直接改变堆中的数据:
(4)当show()调用结束,再次通过地址访问arr时,其值已经被改变:
综上便是对JS数组引用问题的分析,我们凭借这个也可以理解 为什么JS中,对数组元素的操作(类似排序,增删改值等方法)可以很放心的封装起来进行使用,无需返回值;而在C中,此类数组方法 或要在函数里添加返回值,或要动用数组指针才能可以正确使用;
以上 纯为一个小萌新个人分析简介,如有错误或者异议处,烦请指出,感激不尽。