核心概念
-
形参 (Parameters)
- 定义:在函数声明时,写在函数名括号
()里的变量。 - 作用:它们是函数的局部变量,用于接收调用函数时传递过来的值。你可以把它们看作是函数期望接收的输入的“占位符”。
- 生命周期:在函数被调用时创建,在函数执行结束后被销毁。
- 定义:在函数声明时,写在函数名括号
-
实参 (Arguments)
- 定义:在函数调用时,实际传递给函数的具体值或表达式。
- 作用:它们是真正被函数使用的数据。
- 关系:实参会按照顺序依次赋值给对应的形参。
代码示例与分析
让我们通过一个简单的例子来理解:
// 函数定义
function greet(userName, message) { // 这里的 userName 和 message 就是形参
console.log(`Hello ${userName}, ${message}`);
}
// 函数调用
greet('Alice', 'how are you today?'); // 这里的 'Alice' 和 'how are you today?' 就是实参
// 输出:Hello Alice, how are you today?
分析:
- 在定义
greet函数时,我们声明了两个形参:userName和message。 - 在调用
greet函数时,我们传入了两个实参:字符串'Alice'和'how are you today?'。 - 函数开始执行时,会发生类似这样的赋值:
userName = 'Alice';(第一个形参接收第一个实参)message = 'how are you today?';(第二个形参接收第二个实参)
重要特性和常见情况
1. 数量不匹配 (数量不等)
JavaScript 非常灵活,它不强制要求形参和实参的数量必须一致。
- 实参数量 > 形参数量:多余的实参不会被丢弃,但无法通过对应的形参名访问。你可以使用稍后介绍的
arguments对象或 Rest 参数来访问它们。 - 实参数量 < 形参数量:缺少的实参对应的形参值会被设置为
undefined。
function example(a, b) {
console.log(a, b);
}
example(1, 2, 3, 4); // 输出:1, 2 (3和4传入了但未被使用)
example(1); // 输出:1, undefined (b 的值是 undefined)
2. 默认参数 (Default Parameters)
为了解决形参得到 undefined 的问题,ES6 引入了默认参数的特性。允许在定义函数时为形参指定一个默认值。
function multiply(a, b = 1) { // 为形参 b 设置默认值为 1
return a * b;
}
console.log(multiply(5, 2)); // 输出:10 (使用传入的实参 2)
console.log(multiply(5)); // 输出:5 (使用默认值 1,5 * 1 = 5)
console.log(multiply(5, undefined)); // 输出:5 (显式传递 undefined 也会触发默认值)
3. arguments 对象
在普通函数(非箭头函数)内部,你可以访问一个特殊的类数组对象 arguments。它包含了函数调用时传入的所有实参,无论它们是否与形参对应。
function showArgs() {
// 没有定义形参,但依然可以接收实参
console.log(arguments);
console.log(arguments[0]); // 访问第一个实参
console.log(arguments[1]); // 访问第二个实参
// arguments 有 length 属性,但缺乏数组方法如 forEach, map 等。
}
showArgs('a', 'b', 'c');
// 输出:
// Arguments(3) ['a', 'b', 'c', ...]
// a
// b
注意:在现代 JavaScript 中,通常更推荐使用 Rest 参数 (
...) 来代替arguments,因为它是一个真正的数组。
4. Rest 参数 (剩余参数)
使用 ... 语法可以将函数的不确定数量的实参表示为一个数组。Rest 参数必须是最后一个形参。
function sum(...numbers) { // numbers 是一个数组,包含所有传入的实参
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 输出:10
// 也可以结合普通参数使用
function showList(title, ...items) {
console.log(title);
items.forEach(item => console.log('- ' + item));
}
showList('Fruits:', 'Apple', 'Banana', 'Orange');
5. 按值传递 (Pass-by-Value)
JavaScript 中所有函数的参数都是按值传递的。
- 对于原始类型(如
number,string,boolean等),传递的是值的副本。在函数内部修改形参,不会影响外部的实参变量。 - 对于引用类型(如
Object,Array,Function等),传递的是指向对象内存地址的“值”(地址副本)。在函数内部修改形参的属性,会影响到外部的实参对象,因为它们指向同一个对象。但如果尝试重写整个形参(赋予一个新对象),则不会影响外部实参。
// 原始类型:按值传递
function changePrimitive(val) {
val = 100; // 修改只发生在这个函数的局部变量 val 上
console.log('Inside function:', val);
}
let num = 10;
changePrimitive(num); // 输出:Inside function: 100
console.log('Outside function:', num); // 输出:Outside function: 10 (未改变)
// 引用类型:传递地址副本
function changeObject(obj) {
obj.property = 'changed'; // 修改共同指向的那个对象的属性
console.log('Inside function:', obj.property);
}
let myObj = { property: 'original' };
changeObject(myObj); // 输出:Inside function: changed
console.log('Outside function:', myObj.property); // 输出:Outside function: changed (已被改变)
// 尝试重写整个形参(对象)
function rewriteObject(obj) {
obj = { newProperty: 'new' }; // 这里让形参 obj 指向了一个全新的对象,与外部实参断绝了关系
console.log('Inside function:', obj.newProperty);
}
let myObj2 = { property: 'original' };
rewriteObject(myObj2); // 输出:Inside function: new
console.log('Outside function:', myObj2.property); // 输出:Outside function: original (未被改变)
总结对比
| 特性 | 形参 (Parameters) | 实参 (Arguments) |
|---|---|---|
| 定义位置 | 函数声明或表达式的括号 () 中 | 函数调用时的括号 () 中 |
| 本质 | 局部变量,是占位符 | 具体的值或表达式 |
| 数量关系 | 定义函数时确定 | 调用函数时确定,可与形参数量不同 |
| 访问 | 直接通过变量名访问 | 通过形参名或 arguments/Rest 参数访问 |
| 默认值 | 可以使用 ES6 的默认参数语法(如 param = defaultValue) | 无此概念 |
If you enjoyed this, leave a comment~