JavaScript函数参数
在本章节中,我们将学习JavaScript函数参数的相关知识,包括参数的类型、默认值、剩余参数、展开运算符等内容。
1. 函数参数的基本概念
函数参数是函数定义时声明的变量,用于接收函数调用时传递的值。函数参数可以是零个或多个。
// 函数定义时的参数称为形参(形式参数)
function add(a, b) {
// a和b是形参
return a + b;
}
// 函数调用时传递的值称为实参(实际参数)
let result = add(5, 3); // 5和3是实参2. 参数的类型
在JavaScript中,函数参数没有类型限制,可以传递任何类型的值:
// 传递数字
function add(a, b) {
return a + b;
}
let result1 = add(5, 3); // 8
// 传递字符串
let result2 = add("Hello", " World"); // "Hello World"
// 传递布尔值
function isBothTrue(a, b) {
return a && b;
}
let result3 = isBothTrue(true, false); // false
// 传递对象
function getFullName(person) {
return person.firstName + " " + person.lastName;
}
let person = { firstName: "张", lastName: "三" };
let result4 = getFullName(person); // "张三"
// 传递数组
function getFirstElement(arr) {
return arr[0];
}
let numbers = [1, 2, 3];
let result5 = getFirstElement(numbers); // 1
// 传递函数
function calculate(a, b, operator) {
return operator(a, b);
}
let result6 = calculate(5, 3, (x, y) => x * y); // 153. 参数的默认值
ES6(2015)引入了参数默认值的特性,允许为函数参数设置默认值,当调用函数时不传递该参数或传递undefined时,使用默认值。
3.1 基本默认值
// 参数默认值示例
function greet(name = "陌生人") {
console.log(`你好,${name}!`);
}
greet(); // 你好,陌生人!
greet("张三"); // 你好,张三!
greet(undefined); // 你好,陌生人!
greet(null); // 你好,null!(注意:null不会使用默认值)3.2 多个参数的默认值
// 多个参数的默认值
function add(a = 0, b = 0) {
return a + b;
}
console.log(add()); // 0
console.log(add(5)); // 5
console.log(add(5, 3)); // 8
console.log(add(undefined, 3)); // 33.3 表达式作为默认值
默认值可以是表达式,甚至是函数调用:
// 表达式作为默认值
function getCurrentYear() {
return new Date().getFullYear();
}
function createPerson(name, birthYear, currentYear = getCurrentYear()) {
let age = currentYear - birthYear;
return { name, birthYear, age };
}
let person1 = createPerson("张三", 2000); // { name: "张三", birthYear: 2000, age: 26 }(假设当前年份是2026)
let person2 = createPerson("李四", 1995, 2020); // { name: "李四", birthYear: 1995, age: 25 }
// 函数调用作为默认值
function randomNumber() {
return Math.random();
}
function generateId(prefix, suffix = randomNumber()) {
return prefix + suffix;
}
console.log(generateId("id-")); // id-0.123456789
console.log(generateId("id-", "123")); // id-1233.4 默认值的位置
带有默认值的参数应该放在参数列表的末尾:
// 不推荐:带有默认值的参数放在前面
function greet(name = "陌生人", message) {
console.log(`${message},${name}!`);
}
greet(undefined, "你好"); // 你好,陌生人!
greet("张三"); // undefined,张三!(message为undefined)
// 推荐:带有默认值的参数放在后面
function greet(message, name = "陌生人") {
console.log(`${message},${name}!`);
}
greet("你好"); // 你好,陌生人!
greet("你好", "张三"); // 你好,张三!4. 剩余参数
ES6(2015)引入了剩余参数的特性,使用...语法将剩余的实参收集到一个数组中。
4.1 剩余参数的基本使用
// 剩余参数示例
function sum(...numbers) {
return numbers.reduce((total, number) => total + number, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum()); // 04.2 剩余参数与普通参数结合使用
剩余参数必须是参数列表的最后一个参数:
// 剩余参数与普通参数结合使用
function multiply(multiplier, ...numbers) {
return numbers.map(number => number * multiplier);
}
console.log(multiply(2, 1, 2, 3)); // [2, 4, 6]
console.log(multiply(3)); // []
// 错误示例:剩余参数不是最后一个参数
// function test(...numbers, multiplier) {
// // 代码
// }4.3 剩余参数与arguments对象的区别
剩余参数与arguments对象类似,但有以下区别:
- 剩余参数是一个真正的数组,可以使用数组方法;
arguments是一个类数组对象,需要转换为数组才能使用数组方法。 - 剩余参数只包含那些没有对应形参的实参;
arguments包含所有实参。 - 剩余参数是ES6引入的新特性;
arguments是JavaScript的传统特性。
// 剩余参数示例
function test1(...args) {
console.log(args); // [1, 2, 3](数组)
console.log(args instanceof Array); // true
console.log(args.reduce((total, num) => total + num, 0)); // 6
}
test1(1, 2, 3);
// arguments示例
function test2() {
console.log(arguments); // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ](类数组对象)
console.log(arguments instanceof Array); // false
console.log(Array.from(arguments).reduce((total, num) => total + num, 0)); // 6
}
test2(1, 2, 3);5. 展开运算符
展开运算符(Spread Operator)也是ES6引入的新特性,使用...语法,它与剩余参数的语法相同,但作用相反:
- 剩余参数:将多个实参收集到一个数组中
- 展开运算符:将数组或对象展开为多个实参
5.1 展开数组作为函数参数
// 展开数组作为函数参数
let numbers = [1, 2, 3, 4, 5];
console.log(Math.max(...numbers)); // 5
console.log(Math.min(...numbers)); // 1
// 合并数组
let moreNumbers = [6, 7, 8];
let allNumbers = [...numbers, ...moreNumbers]; // [1, 2, 3, 4, 5, 6, 7, 8]
// 函数调用时展开数组
function add(a, b, c) {
return a + b + c;
}
let nums = [1, 2, 3];
console.log(add(...nums)); // 65.2 展开对象作为函数参数
// 展开对象作为函数参数
function createPerson(name, age, city) {
return {
name,
age,
city
};
}
let personInfo = { name: "张三", age: 18 };
let cityInfo = { city: "北京" };
let person = createPerson(...Object.values(personInfo), cityInfo.city); // { name: "张三", age: 18, city: "北京" }
// 合并对象
let person1 = { name: "张三", age: 18 };
let person2 = { city: "北京", ...person1 };
console.log(person2); // { city: "北京", name: "张三", age: 18 }6. 解构赋值作为参数
ES6引入了解构赋值的特性,可以将数组或对象解构为多个变量。解构赋值也可以用于函数参数:
6.1 数组解构作为参数
// 数组解构作为参数
function sum([a, b, c]) {
return a + b + c;
}
let numbers = [1, 2, 3];
console.log(sum(numbers)); // 6
// 带有默认值的数组解构参数
function getFirstTwo([first = 0, second = 0]) {
return [first, second];
}
console.log(getFirstTwo([5, 10])); // [5, 10]
console.log(getFirstTwo([])); // [0, 0]6.2 对象解构作为参数
// 对象解构作为参数
function greet({ name, age }) {
console.log(`你好,我叫${name},今年${age}岁。`);
}
let person = { name: "张三", age: 18 };
greet(person); // 你好,我叫张三,今年18岁。
// 带有默认值的对象解构参数
function createUser({ name = "匿名", age = 0, city = "未知" }) {
return {
name,
age,
city
};
}
console.log(createUser({ name: "李四", city: "上海" })); // { name: "李四", age: 0, city: "上海" }
console.log(createUser({})); // { name: "匿名", age: 0, city: "未知" }
// 为整个参数设置默认值
function createUser2({ name = "匿名", age = 0 } = {}) {
return {
name,
age
};
}
console.log(createUser2()); // { name: "匿名", age: 0 }(不传递参数时使用默认值)7. arguments对象
arguments是一个类数组对象,包含了函数调用时传递的所有实参。arguments对象是函数内部的一个局部变量,只能在函数内部访问。
7.1 arguments对象的基本使用
// arguments对象示例
function test() {
console.log(arguments); // Arguments对象
console.log(arguments.length); // 实参的数量
console.log(arguments[0]); // 第一个实参
console.log(arguments[1]); // 第二个实参
}
test(1, 2, 3); // Arguments(3) [1, 2, 3, ...]7.2 arguments对象的方法
arguments对象有以下方法:
arguments.callee:指向当前正在执行的函数(不推荐使用,在严格模式下被禁用)arguments.caller:指向调用当前函数的函数(不推荐使用,在严格模式下被禁用)arguments.length:返回实参的数量arguments[Symbol.iterator]:返回一个迭代器,可以使用for...of循环遍历
// 使用arguments对象遍历实参
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
// 使用for...of循环遍历arguments对象
function test() {
for (let arg of arguments) {
console.log(arg);
}
}
test(1, 2, 3); // 1 2 37.3 arguments对象与剩余参数的结合使用
// arguments对象与剩余参数的结合使用
function test(...args) {
console.log("剩余参数:", args);
console.log("arguments对象:", arguments);
}
test(1, 2, 3);8. 参数的传递方式
在JavaScript中,函数参数的传递方式是按值传递(Pass by Value):
- 对于基本数据类型(数字、字符串、布尔值、undefined、null、Symbol、BigInt),传递的是值的副本。
- 对于引用数据类型(对象、数组、函数),传递的是引用地址的副本。
8.1 基本数据类型的传递
// 基本数据类型的传递
function changeValue(num) {
num = 10;
console.log("函数内部的num:", num); // 10
}
let x = 5;
changeValue(x);
console.log("函数外部的x:", x); // 5(x的值没有改变)8.2 引用数据类型的传递
// 引用数据类型的传递
function changeObject(person) {
person.name = "李四";
console.log("函数内部的person:", person); // { name: "李四", age: 18 }
}
let person = { name: "张三", age: 18 };
changeObject(person);
console.log("函数外部的person:", person); // { name: "李四", age: 18 }(person的属性被改变)
// 注意:如果在函数内部重新赋值引用类型参数,外部的引用不会改变
function replaceObject(person) {
person = { name: "王五", age: 20 };
console.log("函数内部的person:", person); // { name: "王五", age: 20 }
}
replaceObject(person);
console.log("函数外部的person:", person); // { name: "李四", age: 18 }(person的引用没有改变)9. 函数参数的最佳实践
9.1 限制参数数量
函数参数数量不宜过多,一般不超过3-5个。如果需要传递多个参数,建议使用对象或数组:
// 不推荐:参数数量过多
function createUser(name, age, city, email, phone) {
// 代码
}
// 推荐:使用对象传递多个参数
function createUser(userInfo) {
const { name, age, city, email, phone } = userInfo;
// 代码
}
let userInfo = {
name: "张三",
age: 18,
city: "北京",
email: "zhangsan@example.com",
phone: "13800138000"
};
createUser(userInfo);9.2 使用默认值
为可选参数设置默认值,提高函数的健壮性:
// 推荐:为可选参数设置默认值
function fetchData(url, options = { method: "GET", headers: {} }) {
// 代码
}
fetchData("https://api.example.com/data");
fetchData("https://api.example.com/data", { method: "POST" });9.3 使用剩余参数处理可变数量的参数
使用剩余参数处理可变数量的参数,提高函数的灵活性:
// 推荐:使用剩余参数处理可变数量的参数
function sum(...numbers) {
return numbers.reduce((total, number) => total + number, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 159.4 使用解构赋值简化参数访问
使用解构赋值简化对象或数组参数的访问:
// 推荐:使用对象解构赋值简化参数访问
function greet({ name, age }) {
console.log(`你好,我叫${name},今年${age}岁。`);
}
let person = { name: "张三", age: 18 };
greet(person);10. 常见问题解答
Q: 什么是形参和实参?
A: 函数定义时的参数称为形参(形式参数),函数调用时传递的值称为实参(实际参数)。
Q: JavaScript函数参数的传递方式是什么?
A: JavaScript函数参数的传递方式是按值传递:对于基本数据类型,传递的是值的副本;对于引用数据类型,传递的是引用地址的副本。
Q: 什么是参数默认值?
A: 参数默认值是ES6引入的特性,允许为函数参数设置默认值,当调用函数时不传递该参数或传递undefined时,使用默认值。
Q: 什么是剩余参数?
A: 剩余参数是ES6引入的特性,使用...语法将剩余的实参收集到一个数组中。
Q: 剩余参数和arguments对象有什么区别?
A: 剩余参数是一个真正的数组,可以使用数组方法;arguments是一个类数组对象,需要转换为数组才能使用数组方法。剩余参数只包含那些没有对应形参的实参;arguments包含所有实参。
Q: 什么是展开运算符?
A: 展开运算符是ES6引入的特性,使用...语法将数组或对象展开为多个实参。
Q: 什么是解构赋值作为参数?
A: 解构赋值作为参数是指在函数定义时,使用数组或对象解构语法将参数解构为多个变量。
11. 练习项目
创建一个HTML文件,包含以下内容:
- 一个输入框,用于输入多个数字(用逗号分隔)
- 一个按钮,用于计算
- 一个显示结果的区域
使用JavaScript实现以下功能:
- 定义一个函数,接受多个数字作为参数
- 计算这些数字的和、平均值、最大值和最小值
- 使用剩余参数处理可变数量的参数
- 使用展开运算符将输入的字符串转换为数字数组
- 在结果区域显示计算结果
测试不同的输入值,确保计算结果正确
12. 小结
- 函数参数是函数定义时声明的变量,用于接收函数调用时传递的值
- JavaScript函数参数没有类型限制,可以传递任何类型的值
- ES6引入了参数默认值的特性,允许为函数参数设置默认值
- 剩余参数使用...语法将剩余的实参收集到一个数组中
- 展开运算符使用...语法将数组或对象展开为多个实参
- 解构赋值可以用于函数参数,简化对象或数组参数的访问
- arguments是一个类数组对象,包含了函数调用时传递的所有实参
- JavaScript函数参数的传递方式是按值传递:对于基本数据类型,传递的是值的副本;对于引用数据类型,传递的是引用地址的副本
- 函数参数的最佳实践包括:限制参数数量、使用默认值、使用剩余参数处理可变数量的参数、使用解构赋值简化参数访问
在下一章节中,我们将学习JavaScript函数返回值的相关知识。