4.3 参数
函数可以使用参数来传递数据,也可以不使用参数。调用函数时,传递给函数的值被称为“函数的实参”(入参),对应位置的函数参数名为“形参”。
例如:
在上面的示例中,a和b为形参,1和2为实参。
形参与实参的位置是一一对应的,如果实参的个数小于形参的个数,剩余的形参默认值为undefined,示例如下。
foo(1);
// > 1 // > undefined
4.3.1 按值传递
参数是按值传递的,如果参数是一个包含原始值(数字、字符串、布尔值)的变量,则就算函数在内部改变了参数的值,该参数在函数外的值也不会改变,示例如下。
上述代码中,我们在函数内部改变了传入的a的值,但函数执行完毕后,变量a的值没有发生任何变化,这表示传入函数内部的仅仅是变量a的值,而不是变量a。
如果参数是一个引用类型的数据,则形参和实参指向同一个对象,假如在函数内部改变了形参的值,实参指向的对象的值也会改变,示例如下。
但如果改变的是形参的地址,则实参与形参地址不同,不再指向同一个对象,示例如下。
上述代码中,在函数内部,我们直接为参数obj赋值了一个新的地址,然后为函数内部的变量obj添加值为"0"的name属性,在执行函数foo时,打印出参数obj的值{name: "0"},然后打印变量obj的值,两者的值不一致。这也就是说,在修改了形参的地址后,实参中所存储的地址并没有发生变化,从而证明了引用类型的数据时传递的是地址值,而不是引用值,否则在修改形参的地址后,实参的地址也会发生改变。
4.3.2 arguments
我们可以在定义函数时为函数指定参数,示例如下。
但有时,我们无法确定需要传递的参数的个数,这时就需要有一种方式获取所有参数,JavaScript提供了一个类数组的对象arguments,以获取传递给函数的参数。
看起来arguments对象是一个数组,它拥有length属性,而且可以通过下标来访问,但实际上,arguments对象除了这两个特性外,并没有数组的实例方法,如果在其上调用数组的实例方法,程序将会出错,示例如下。
arguments可以被转化为一个真正的数组,示例如下。
// 两种方式
Array.prototype.slice.call(arguments);
Array.from(arguments);
4.3.3 Rest参数(剩余参数)
除了Array.from,Rest参数也可以将参数转化为数组,示例如下。
Rest参数可以不必包含所有的实参,相比arguments,其更加自由,示例如下。
注意:Rest参数之后不能有其他参数。
Rest参数是一个数组,这意味着它也可以被解构,示例如下。
4.3.4 箭头函数中的arguments对象
箭头函数中是不能使用arguments的,在箭头函数中,arguments将会被当作一个普通的变量,示例如下。
let foo = ()=> console.log(arguments);
foo(1); // -> Uncaught ReferenceError: arguments is not defined 如果需要使用类似 arguments 的功能,可以使用 Rest 参数来代替:
let foo = (...args)=> console.log(args);
foo(1); // > [1]
4.3.5 默认参数
ES6中加入了默认参数,以便于在定义函数时为参数指定默认值。在此之前,我们通常像下面这样为函数参数指定默认值。
得益于默认参数,上述代码可以简化如下。
function foo(a = 1, b = 2){ console.log(a, b); }
在上文中,我们已经指定形参的默认值为undefined,而默认参数生效的条件就是值为undefined时才生效,示例如下。
练习
- 使用function定义一个函数,接收多个数字作为参数,返回第一个参数与其他参数的和的积。
- 为该函数指定默认参数值。
- 将该函数修改为箭头函数。