形参与实参

Published 2025-09-14 09:34 1406 words 8 min read

This post is not yet available in English. Showing the original.
核心概念 形参 (Parameters) 定义:在函数声明时,写在函数名括号 里的变量。 作用:它们是函数的局部变量,用于接收调用函数时传递过来的值。你可以把它们看作是函数期望接收的输入的“占位符”。 生命周期:在函数被调用时创建,在函数执行结束后被销毁。 实参 (Arguments) 定义:在函数调用时,实际传递给函数的具体值或表达式。 作用:它们是真正被函数使用的数据。...

核心概念

  1. 形参 (Parameters)

    • 定义:在函数声明时,写在函数名括号 () 里的变量。
    • 作用:它们是函数的局部变量,用于接收调用函数时传递过来的值。你可以把它们看作是函数期望接收的输入的“占位符”。
    • 生命周期:在函数被调用时创建,在函数执行结束后被销毁。
  2. 实参 (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 函数时,我们声明了两个形参:userNamemessage
  • 在调用 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~