JS 函数调用时的参数(形参、实参)浅析

 开门见山,先考虑下面这个问题

1  var color = 'red';
2  var style = {width: 200, height: 300};
3  var colorList = ['black', 'red'];
4  function change (color, style, colorList) {
5     color = 'black'
6     style = {width: 400, height: 600};
7     colorList.push('green')
8  }
9  change(color, style, colorList)
10 console.log(color, style, colorList) // red {width: 200, height: 300} ["black", "red", "green"]

 首先,在不知道输出结果条件下,我们可以先假设函数调用执行时传入的参数(即第9行)即为函数定义时的参数(第4行)。通过函数内部对三个参数的修改(5,6,7行),在最后的输出时(10行),结果应该是修改后的值,即

color => black 
style => {width: 400, height: 600}
colorList => ["black", "red", "green"]

但是通过对比,我们发现这种假设得出的结果与实际的输出不符。

其次,顺着这个思路我们可以再次假设函数执行时的参数只是传入参数的值的一份拷贝,即函数外部的值在函数执行时与函数内部的值无关,只是值的一份拷贝,这种假设的输出应为参数定义时赋予的值,即

color => red 
style => {width: 200, height: 300}
colorList => ["black", "red"]

通过对比,color和style的值与正确的值一致,但是colorList的值却出现异常,为什么函数内部对参数的修改有时会生效有时却无效呢?函数调用时的参数和函数定义时的参数有什么不同呢?他们的值是怎样的对应关系呢?

带着这些疑问,我们查阅资料,JS中参数有形参和实参,且分为不同的数据类型,在《Javascript权威指南》中这样定义:

       参数有形参(parameter)和实参(argument)的区别,形参相当于函数中定义的变量,实参是在运行时的函数调用时传入的参数。

W3C中数据类型 

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型:对象(Object)、数组(Array)、函数(Function)。 

此时,我们知道

var color = 'red';
var style = {width: 200, height: 300};
var colorList = ['black', 'red'];
function change (color, style, colorList) { // 此处的color, style, colorList 为形参,在函数调用时才会有具体的值
    color = 'black'
    style = {width: 400, height: 600};
    colorList.push('green')
}
change(color, style, colorList) // 此处的color, style, colorList 为实参
console.log(color, style, colorList)

那形参和实参在实际的函数执行中是怎么样的关系呢?形参和实参是键对应还是顺序对应呢?形参和实参的数据类型必须一致吗?函数内部引用的是实参还是形参呢?下面我们一起来通过实践探讨这些问题

首先,从简单的对应关系入手

var color = 'red'
var size = 'big'
var el = 'table'
function paramTest (color, size, el) {
    console.log(color, size, el)
    red.push('s') // Uncaught TypeError: red.push is not a function
}
paramTest(color, size, el) // 'red' 'big' 'table'
paramTest(size, el, color) // 'big' 'table' 'red'
paramTest(el, color, size) // 'table' 'red' 'big'

很好,从结果来看,形参和实参是顺序对应,与形参和实参的键无关,并且,形参在要使用对应数据类型方法的情况下,必须有与之匹配的数据类型。当然,我们日常编码过程中,最好能一眼辨认出形参与实参的对应关系,不然你的leader可能会揍你!!

接下来,我们来探讨一下函数内部引用的是实参还是形参,即使此时你心里隐约有了定论,实践出真知嘛,

1     var color = 'red'
2     var size = 'big'
3     var el = 'table'
4     function paramTest (a, b, c) {
5         console.log(a, b, c) // red big table
6         a = 'a' 
7         b = 'b'
8         c = 'c'
9         console.log(a, b, c) // a b c
10    }
11    paramTest(color, size, el)
12    console.log(color, size, el) // red big table

根据对函数内部的参数修改以及打印结论,我们得知,函数内部引用的是形参,当参数类型为基本类型时,形参是对实参值的一份拷贝,那么如果参数类型为引用类型呢?

var style = {width: 200, height: 300};
var colorList = ['black', 'red'];
function change (style, colorList) {
    style.width = 400 
    style.height = 600
    colorList.push('green')
}
change(style, colorList)
console.log(style, colorList) // {width: 200, height: 300} ['black', 'red', 'green']

此时的结果表明,在函数内部修改这个引用时,函数外部的引用也发生了变化,此时有两种可能,一是形参拷贝了实参的指针,另一种是形参拷贝了这个引用本身。

现在,我们可以回到最初的那个问题,寻找最终的答案

var color = 'red';
var style = {width: 200, height: 300};
var colorList = ['black', 'red'];
function change (color, style, colorList) {
    color = 'black'
    style = {width: 400, height: 600};
    colorList.push('green')
}
change(color, style, colorList)
console.log(color, style, colorList) // red {width: 200, height: 300} ["black", "red", "green"]

由最终的结论可知,以上的第一种推论正确,形参只是拷贝了实参的指针指向,他们都是一个指向一块存有引用类型数据的指针。我们来逐条分析最终的结果

color 为基本类型,形参是实参的一份拷贝,所以函数内的color与函数外的color是两个变量,指向不同的内存区域,只是内存内的值都为'red',当函数内的color修改自身的值时,只是修改了自己的内存值,并不会影响外部的color值

style 为引用类型(Object),所以形参是实参指针的拷贝,他们都指向同一内存块。但当函数内部的style使用'='号赋值时,实际上修改的是自身的指针指向时,函数内style的指针指向发生改变,此时内外两个style的内存指向不同,因此不会影响外部对象的值

colorList 为引用类型(Array),所以形参是实参指针的拷贝,他们都指向同一内存块。在形参没有修改自身指针指向的情况下,对形参的修改即对形参实参指向的统一内存块内值进行修改,即函数内部的修改会响应到外部对象的变量,实际上修改的就是同一内存内的值。

至此,我们已经完全分析了这道题的来龙去脉。做一下总结

  •  参数有形参(parameter)和实参(argument)的区别,形参相当于函数中定义的变量,实参是在运行时的函数调用时传入的参数。
  • 形参和实参的对应关系为顺序对应,且形参与实参的数据类型在使用上必须保持一致,否则会发生“类型不匹配”的错误。
  • 当实参为基本数据类型时,在该函数运行时,形参和实参是不同的变量,形参是实参的一份拷贝。
  • 当实参是引用类型变量,在调用该函数的过程中,传给函数的是实参的地址指针,形参是实参的地址指针拷贝

版权声明:本文为qq_34937720原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。