函数中的参数传递是所有编程语言的中的基础内容。本文是介绍了js中保存基本数据类型和引用数据类型的两种方式以及在函数传参的本质。
1 简介
本文介绍了js中两种不同数据类型的数据的保存问题,以及赋值操作的本质,最后指出函数传参就是赋值操作,并用图形的方式介绍了参数传递的过程。
本文属于基础内容。你可以先检查看下如下练习题,如果很轻易就可以完成,就不必往下看啦。
2 练习题
以下有6道题。
题1
var a = [1];
var b = a;
b = [10]
console.info(a)
console.info(b)
题2
var a = [1];
var b = a;
b[1] = 10
console.info(a)
console.info(b)
题3
var a = [1];
function f(a){
a = [10]
}
f(a);
console.info(a)
题4
var a = 1;
function f(a){
a = 10
}
f(a);
console.info(a)
题5
var a = [1];
function f(a){
a[1] = 10
}
f(a);
console.info(a)
题6
var a = [1];
function f(a){
a[1] = 10
a = 3;
}
f(a);
console.info(a)
答案:
题1:a:[1], b: [10]
题2:a:[1,10], b: [1,10]
题3:a:[1]
题4:a: 1
题5:a: [1,10]
题6:a: [1,10]
3 内容
•数据的保存方式•赋值运算符的含义•参数传递的本质•例题讲解
4 数据的保存方式
js中的数据类型分成两大类:基本数据类型和引用数据类型。不论是何种数据类型都需要有一个地方把数据的内容保存下来,在我们给变量赋值的过程中,就是在做保存的过程。
在如下两行代码中,我们分别把一个基本数据类型的数据和引用类型的数据保存在两个变量中,
var a = [10];
var b = 1;
我们先回顾数据保存的方式:
(1)基本数据类型的类型保存在栈区;
(2)引用数据类型的数据要用到两个区域:在栈区保存的是一个地址信息,而在堆区保存着真正的数据,这个堆区的地址就在栈区中保存着。
所以上面两句代码对应的效果是:
5 赋值运算符的含义
赋值运算符是我们最常用的运算符:=
。当我们在写一个赋值运算符时,其实背后的工作还是很多的。=的左边是变量(或者是常量),而等号的右边的情况会复杂一些:
-
是值
-
是变量
-
是函数
5.1 右边是值
varName = value;
它表示把等号右边的值保存到等号左边的变量(或者是常量)中。
背后做的事有:
1.检查等号左边的这个变量是否存在。如果不存在,是否允许创建这个变量。这一步没有问题了,再去看右边。
2.把右边的值找个地方存起来,此时就要根据右边的值是引用类型还是基本数据类型来决定如何去保存它了,如果是基本数据类型,则直接保存在栈区;如果是引用类型,则先在堆区中保存数据,再把地址保存在栈区。
5.2 右边是变量
varName = var1;
它表示把等号右边的变量中保存的值赋值到等号左边的变量(或者是常量)中。是直接把栈区中的保存的内容进行赋值。
5.3 右边是函数调用
varName = 函数调用();
先把函数执行完成,再把函数的返回值保存在左边的变量中。
var obj ={}
function f(){
alert(1)
}
obj.a.b = f(); //这里会报错,因为obj.a不存在。
如上代码中可以发现,虽然第5句代码不能正确赋值,但函数f还是执行了的。
5.4 例子
我们来看一个简单的例子:
var a = [1];
var b = a; // 注意这一句
以上的赋值操作是把变量a在栈区的内容保存到了变量b中,所以导致a,b指向同一个数组。
接下来,给通过下标给b赋值
var a = [1];
var b = a;
b[1] = 10; // 把10保存在第二个元素中。
结果如下:
很明显,数组的内容发生了变化。
再接下来,重新给b赋值
var a = [1];
var b = a;
b[1] = 10;
b = [10]; // 把数组保存在b中
b=[10] 的运行结果是:
(1)找个地方保存数组,由于数组是引用数据类型,所以需要栈区和堆区一起配合。
(2)把地址保存在变量b中。从a,b不再指向同一个数组,它们从此是路人。
6 函数中的参数传递
6.1 本质
在函数的调用过程中,存在传值的过程 具体来说,是实参的值传给形参。
我们可以这样来理解这个过程:
•函数的形参就是函数内部定义的局部变量。•实参传递给形参的过程就是一个赋值过程。•函数调用结束后,局部变量会被回收。
6.2 例子
下面来看一个例子
var a = [1];
function f(a){
a[1] = 10
a = [10]
}
f(a);
console.info(a)
分步骤来讲解这个过程。
初始情况下:
在调用f(a)时,相当于是做了一次赋值操作:把全局变量a的值赋值给局部变量a(赋值的内容是栈中的内容),结果是它们指一个数组。
下面执行函数体中的第一句:
下面执行函数体中的第二句:
最后一步:
函数调用结束,释放函数中的变量及对应的空间。
函数调用完毕,释放空间。局部变量a,及f-堆中的数组全回收了。此时访问的a是全局变量。
7 小结
•基本数据类型只需要用到栈区;引用数据类型需要用到栈区和堆区;•赋值是把右边的值保存到左边的变量中;赋值的过程中传递的栈区的内容;•函数中的形参相当于函数内部的局部变量 ;实参传给形参的过程就是赋值的过程;•函数调用完成后,它的局部变量会随之销毁,除非遇到特殊情况(例如闭包)