← JavaScript函数 JavaScript默认参数 →

JavaScript函数参数

原创 2026-03-12 JavaScript 已有人查阅

JavaScript函数参数详解:从基础到进阶的完整指南

在JavaScript开发中,函数参数是我们每天都在打交道的核心概念。无论你是刚入门的新手,还是写了多年代码的老手,对函数参数的理解深度直接决定了代码的质量和灵活性。今天,我想和大家深入聊聊JavaScript函数参数的方方面面。

什么是函数参数?

函数参数就是定义在函数括号中的变量,它们充当着函数与外部世界通信的桥梁。当调用函数时,我们传入的 arguments(实参)会赋值给 parameters(形参),让函数能够处理外部传入的数据。

// 定义函数时,name 和 age 就是参数(形参)
function introduce(name, age) {
    console.log(`我叫${name},今年${age}岁`);
}

// 调用函数时,'张三' 和 25 就是实参
introduce('张三', 25);

JavaScript参数的核心特性

1. 动态类型特性

JavaScript是一种动态类型语言,这一点在函数参数上体现得淋漓尽致。我们不需要像Java或C++那样提前声明参数类型:

function processValue(value) {
    // value 可以是任何类型
    console.log(typeof value, value);
}

processValue(42);           // number 42
processValue('Hello');      // string Hello
processValue(true);         // boolean true
processValue({name: '李四'}); // object { name: '李四' }
processValue([1,2,3]);      // object [1,2,3]

个人经验分享:这个特性虽然带来了灵活性,但也增加了代码的不确定性。在项目中,我强烈建议使用TypeScript或者在函数开头进行类型检查,尤是在开发团队协作的项目时。

2. 参数数量灵活性

JavaScript函数调用时,实参和形参的数量不需要严格匹配:

function greet(firstName, lastName) {
    console.log(`参数数量:${arguments.length}`);
    console.log(`firstName: ${firstName}, lastName: ${lastName}`);
}

greet('王');                    // 只传一个参数
greet('王', '小明', '额外参数');  // 传三个参数

默认参数(Default Parameters)

在ES6之前,我们判断参数是否传入需要写额外的代码。ES6引入的默认参数让代码更优雅:

// 旧写法
function multiplyOld(a, b) {
    b = (typeof b !== 'undefined')  b : 1;
    return a * b;
}

// ES6新写法
function multiplyNew(a, b = 1) {
    return a * b;
}

console.log(multiplyNew(5));    // 5,b使用默认值1
console.log(multiplyNew(5, 3)); // 15

注意:默认参数有个细微的特性 - 只有传入undefined或者不传时才会使用默认值。传入null不会触发默认值:

function test(value = '默认值') {
    console.log(value);
}

test(undefined);  // 默认值
test(null);       // null(不会使用默认值)
test();           // 默认值

剩余参数(Rest Parameters)

剩余参数是ES6带来的另一个重要特性,它让我们能优雅地处理可变数量的参数:

function calculateAverage(description, ...scores) {
    // scores 是一个数组,包含所有额外的参数
    console.log(description);
    
    if (scores.length === 0) {
        return 0;
    }
    
    let sum = scores.reduce((total, score) => total + score, 0);
    return sum / scores.length;
}

console.log(calculateAverage('张三的成绩:', 85, 90, 78, 92));
console.log(calculateAverage('李四的成绩:', 88, 76));

为什么用剩余参数而不是arguments对象?
剩余参数有几个明显优势:

  1. 剩余参数是真正的数组,可以使用数组的所有方法

  2. 剩余参数只包含没有对应形参的实参

  3. 剩余参数使代码意图更清晰

arguments对象的正确认识

每个非箭头函数都有自己的arguments对象,它是一个类数组对象:

function showArguments() {
    console.log('arguments类型:', Object.prototype.toString.call(arguments));
    console.log('arguments长度:', arguments.length);
    
    // 转换为数组的几种方式
    let args1 = Array.from(arguments);
    let args2 = [...arguments];
    let args3 = Array.prototype.slice.call(arguments);
    
    console.log('转换后的数组:', args1);
}

showArguments('前端开发', 'JavaScript', 'Node.js');

个人见解:虽然arguments仍然可用,但在新项目中我更推荐使用剩余参数。arguments在箭头函数中不可用,且操作上不如数组方便。

传值vs传引用:一个需要深入理解的概念

JavaScript中的参数传递机制常常让初学者困惑,实规则很简单:原始类型传值,对象类型传引用

原始类型传值

function modifyPrimitive(value) {
    value = 100;  // 这里修改的是局部变量的值
    console.log('函数内部:', value);
}

let num = 50;
modifyPrimitive(num);
console.log('函数外部:', num);  // 50,原值不变

对象类型传引用

function modifyObject(obj) {
    obj.name = '修改后的名称';  // 修改对象的属性
    obj.age = 30;               // 添加新属性
    
    // 尝试重新赋值整个对象
    obj = { brand: '全新对象' };
    console.log('函数内部重新赋值:', obj);
}

let user = { name: '赵六', age: 25 };
console.log('调用前:', user);
modifyObject(user);
console.log('调用后:', user);  // name已被修改,age被添加

关键理解:当传递对象时,传递的是对象引用的副本。这意味着:

  • 通过这个引用可以修改原对象的属性

  • 但重新赋值整个对象不会影响原对象

参数解构:现在JavaScript的必备技巧

解构赋值让函数参数处理更加优雅:

// 对象参数解构
function createUser({name, age, email = '未提供'}) {
    console.log(`用户名:${name},年龄:${age},邮箱:${email}`);
}

createUser({name: '钱七', age: 28});

// 数组参数解构
function processCoordinates([x, y, z = 0]) {
    console.log(`X: ${x}, Y: ${y}, Z: ${z}`);
}

processCoordinates([10, 20]);
processCoordinates([5, 15, 25]);

// 嵌套解构
function displayConfig({server: {host, port}, database}) {
    console.log(`服务器:${host}:${port}`);
    console.log(`数据库:${database}`);
}

displayConfig({
    server: { host: 'localhost', port: 3306 },
    database: 'myapp'
});

高阶应用:参数与回调函数

在开发中,函数参数经常是他函数(回调):

function fetchData(url, onSuccess, onError) {
    // 模拟异步请求
    setTimeout(() => {
        try {
            let data = { id: 1, content: '模拟数据' };
            onSuccess(data);
        } catch (error) {
            onError('数据获取失败');
        }
    }, 1000);
}

fetchData('/api/data',
    (data) => console.log('成功:', data),
    (error) => console.error('失败:', error)
);

参数验证的实践

考虑到JavaScript的动态特性,适当的参数验证很重要:

function calculateDiscount(price, discountPercent) {
    // 参数类型验证
    if (typeof price !== 'number' || price < 0) {
        throw new Error('价格必须是正数');
    }
    
    if (discountPercent === undefined) {
        discountPercent = 0; // 无折扣
    }
    
    if (typeof discountPercent !== 'number' || 
        discountPercent < 0 || 
        discountPercent > 100) {
        throw new Error('折扣率必须是0-100之间的数字');
    }
    
    return price * (1 - discountPercent / 100);
}

try {
    console.log(calculateDiscount(100, 20));  // 80
    console.log(calculateDiscount('100', 20)); // 抛出错误
} catch (error) {
    console.error('计算错误:', error.message);
}

课程知识要点

  1. 参数定义:在函数括号中声明,作为函数内部使用的变量

  2. 默认参数:使用"="为参数提供默认值,只有传入undefined时才生效

  3. 剩余参数:用"..."语法收集剩余所有参数为数组

  4. arguments对象:函数内部的类数组对象,包含所有传入参数

  5. 传值机制:原始类型传副本,对象类型传引用

  6. 参数解构:直接从对象或数组中提取值作为参数

  7. 类型灵活性:JavaScript不强制参数类型,但建议进行验证

课后思考

函数参数看似基础,但深入理解后会发现很多精妙之处。合理运用这些特性,能让代码更简洁、更健壮。在开发中,我的建议是:

  • 尽量使用默认参数代替手动检查undefined

  • 用剩余参数替代arguments,代码更清晰

  • 重要函数添加参数验证,避免运行时错误

  • 合理使用解构,让参数意图更明确

← JavaScript函数 JavaScript默认参数 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号