JavaScript 函数传递参数与值引用
向函数传递参数,我们可以创建可重用且动态的代码。通过传递参数,我们可以每次在调用函数时定制它的行为,使我们的函数更加通用,能够适应不同的情况。
本章节将探讨如何有效地向函数传递参数,确保你扎实地理解这一核心编程原则。
1. 理解实参 (Arguments) 和形参 (Parameters)
实参 (Arguments) 是你在调用函数时传递给它的实际值。
形参 (Parameters) 是函数定义中圆括号内列出的变量。
形参充当了将来被传入的实参的占位符。
看看这个比喻:想象一台自动售货机(函数)。机器上的按钮就是形参(例如 "A1", "B2", "C3")。当你按下一个按钮(调用函数)时,你提供了一个实参——即你想要的具体选择。然后机器会根据那个按钮分发对应的商品。
// 带参数的函数定义:
// name 和 greeting 是形参
function greet(name, greeting) {
console.log(greeting + ", " + name + "!");
}
// 带参数的函数调用:
// "爱丽丝" 和 "你好" 是第一次调用时传递的实参
greet("爱丽丝", "你好"); // 输出: 你好, 爱丽丝!
// "鲍勃" 和 "早上好" 是再次调用时传递的实参
greet("鲍勃", "早上好"); // 输出: 早上好, 鲍勃!在上面的例子中:
name和greeting是greet函数的 形参 (Parameters)。- "
爱丽丝" 和 "你好" 是函数第一次被调用时传入的 实参 (Arguments)。 - "鲍
勃" 和 "早上好" 是函数再次被调用时传入的 实参 (Arguments)。
2. 传递不同的数据类型作为参数
JavaScript 是一种动态类型语言,这意味着你在定义函数时不需要显式声明参数的数据类型。你可以传递各种数据类型作为参数:
function displayInfo(name, age, isStudent) {
console.log("姓名: " + name);
console.log("年龄: " + age);
console.log("是否为学生: " + isStudent);
}
displayInfo("查理", 20, true);
// 输出:
// 姓名: 查理
// 年龄: 20
// 是否为学生: true
displayInfo("达娜", 25, false);
// 输出:
// 姓名: 达娜
// 年龄: 25
// 是否为学生: false你也可以传递对象和数组作为参数:
function printAddress(address) {
console.log("地址: " + address.street + ", " + address.city + ", " + address.country);
}
const myAddress = {
street: "主街 123 号",
city: "任意城",
country: "美国"
};
printAddress(myAddress); // 输出: 地址: 主街 123 号, 任意城, 美国
function sumArray(numbers) {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
const numberList = [1, 2, 3, 4, 5];
const total = sumArray(numberList);
console.log("总和: " + total); // 输出: 总和: 153. 实参数量 vs. 形参数量
JavaScript 允许你调用函数时,传入的实参数量多于或少于函数定义时的形参数量。
参数过少: 如果你传入的实参少于形参,缺失的形参将被赋值为 undefined。
function describePerson(name, age, occupation) {
console.log("姓名: " + name);
console.log("年龄: " + age);
console.log("职业: " + occupation);
}
describePerson("伊芙", 30);
// 输出:
// 姓名: 伊芙
// 年龄: 30
// 职业: undefined参数过多: 如果你传入的实参多于形参,多余的实参可以通过函数内部的 arguments 对象访问(虽然这是一个旧特性,现在更推荐使用剩余参数 rest parameters,我们稍后会学到)。
function displayFirstTwo(a, b) {
console.log("第一个: " + a);
console.log("第二个: " + b);
console.log("参数总数: " + arguments.length);
}
displayFirstTwo(1, 2, 3, 4, 5);
// 输出:
// 第一个: 1
// 第二个: 2
// 参数总数: 54. 按值传递 vs. 按引用传递
在 JavaScript 中,根据参数的数据类型不同,参数传递给函数的方式分为按值传递 (Passing by Value) 和 按引用传递 (Passing by Reference)。
4.1 按值传递 (Passing by Value)
原始数据类型(如数字 number、字符串 string、布尔值 boolean、null 和 undefined)是按值传递的。
这意味着会创建参数值的一个副本并传给函数。在函数内部对参数进行修改,不会影响函数外部的原始参数。
function changeValue(x) {
x = 10;
console.log("函数内部: x = " + x); // 输出: 函数内部: x = 10
}
let y = 5;
changeValue(y);
console.log("函数外部: y = " + y); // 输出: 函数外部: y = 5在这个例子中,即使我们在 changeValue 函数内部改变了 x 的值,函数外部 y 的值依然保持不变。
4.2 按引用传递 (Passing by Reference)
对象(包括数组和函数)是按引用传递的。
这意味着函数接收的是指向内存中原始对象的引用(或指针),而不是对象本身的副本。在函数内部对对象属性的修改将会影响函数外部的原始对象。
function changeObject(obj) {
obj.name = "鲍勃";
console.log("函数内部: obj.name = " + obj.name); // 输出: 函数内部: obj.name = 鲍勃
}
let myObj = { name: "爱丽丝" };
changeObject(myObj);
console.log("函数外部: myObj.name = " + myObj.name); // 输出: 函数外部: myObj.name = 鲍勃在这个例子中,changeObject 函数修改了 myObj 对象的 name 属性。因为对象是按引用传递的,所以这个改变也反映在函数外部。
重要提示: 如果你在函数内部重新给对象本身赋值(例如 obj = { name: "查理" };),你是在函数作用域内创建了一个新对象,函数外部的原始对象不会改变。function reassignObject(obj) {
obj = { name: "查理" };
console.log("函数内部: obj.name = " + obj.name); // 输出: 函数内部: obj.name = 查理
}
let myOtherObj = { name: "爱丽丝" };
reassignObject(myOtherObj);
console.log("函数外部: myOtherObj.name = " + myOtherObj.name); // 输出: 函数外部: myOtherObj.name = 爱丽丝5. 实战示例
让我们看一些向函数传递参数的实用例子。
5.1 计算矩形面积
function calculateArea(length, width) {
const area = length * width;
return area;
}
let rectangleLength = 10;
let rectangleWidth = 5;
let area = calculateArea(rectangleLength, rectangleWidth);
console.log("矩形的面积是: " + area); // 输出: 矩形的面积是: 505.2 格式化姓名
function formatName(firstName, lastName, format) {
if (format === "FL") {
return firstName + " " + lastName;
} else if (format === "LF") {
return lastName + ", " + firstName;
} else {
return "格式无效";
}
}
let formattedName1 = formatName("John", "Doe", "FL");
console.log(formattedName1); // 输出: John Doe
let formattedName2 = formatName("Jane", "Smith", "LF");
console.log(formattedName2); // 输出: Smith, Jane
let formattedName3 = formatName("Peter", "Jones", "XYZ");
console.log(formattedName3); // 输出: 格式无效5.3 更新库存
假设你正在构建一个电商应用。你可以创建一个函数来更新产品的库存:
let inventory = {
"ProductA": 10,
"ProductB": 5,
"ProductC": 20
};
function updateInventory(productName, quantityChange) {
if (inventory.hasOwnProperty(productName)) {
inventory[productName] += quantityChange;
} else {
console.log("库存中未找到该产品。");
}
}
updateInventory("ProductA", -3); // 卖出 3 个 ProductA
console.log(inventory["ProductA"]); // 输出: 7
updateInventory("ProductC", 5); // 增加 5 个 ProductC
console.log(inventory["ProductC"]); // 输出: 25
updateInventory("ProductD", 10); // 尝试更新一个不存在的产品
// 输出: 库存中未找到该产品。在这个例子中,我们将产品名称(字符串)和数量变化(数字)作为参数传递给 updateInventory 函数。该函数直接修改了 inventory 对象,因为对象是按引用传递的。