找回密码
 立即注册
首页 业界区 业界 Web前端入门第 65 问:JavaScript 函数参数各种使用方式 ...

Web前端入门第 65 问:JavaScript 函数参数各种使用方式

猷浮 2025-6-13 08:27:15
函数参数是什么?
就是函数内部无法确定的一个东西,需要外部传给函数内部的玩意儿,语法上就是写在函数括号中的东东。比如:
  1. function test(a) {}
复制代码
其中的 a 就是 test 函数的参数,在函数体内部,a 作为一个变量存在,可以修改它。
JS 的函数参数,真的是可以传入任意值,没有任何限制,可以包括 原始类型、对象,数组,函数 等等,只要是 JS 语言支持的,都可以当做参数传入。
原始类型

JS 原始类型参数(number、string、boolean、null、undefined、symbol、bigint)按值传递,传入的是值的副本,在函数里面修改传入的值不会影响外部变量。
  1. function test(a) {
  2.   a = '前端路引'; // 修改原始类型不会影响 arg 变量值
  3. }
  4. let arg  = '微信公众号';
  5. test(arg);
  6. console.log(arg); // 输出 '微信公众号'(原值未改变)
复制代码
引用类型

JS 引用类型参数(对象、数组、函数)按引用地址传递,如果函数里面修改了对象属性,会影响外部变量,使用时需特别注意!!
  1. function test (obj) {
  2.   obj.name = '前端路引' // 修改了对象属性,会影响共享的对象
  3.   obj = { // 如果直接给 obj 参数赋值,不会影响共享的对象,因为 obj 已经变成了一个新的对象
  4.     test: '测试参数'
  5.   }
  6. }
  7. const weChat = {
  8.   type: '微信公众号',
  9.   age: 1,
  10. };
  11. test(weChat);
  12. console.log(weChat) // {type: '微信公众号', age: 1, name: '前端路引'}
复制代码
默认参数

ES6 版本为 JS 注入了一大堆活性,各种花活不断,默认参数就是其中一个最常用的花活。当未传入参数或者传入的参数是 undefined,则使用默认参数。
  1. function test(a = '前端路引') {
  2.   return a; // 将 a 变量值返回出去
  3. }
  4. let arg  = '微信公众号';
  5. console.log(test(arg)); // 输出 '微信公众号'
  6. // 没传入参数,使用默认值
  7. console.log(test()); // 输出 '前端路引'
  8. // 传入 undefined 也是用默认值
  9. console.log(test(undefined)); // 输出 '前端路引'
复制代码
剩余参数

ES6 的又一花活之一,允许使用 ... 语法,将多余的参数合并为数组,在箭头函数中可以代替 arguments 对象。
  1. function sum(...numbers) {
  2.   return numbers.reduce((acc, num) => acc + num, 0);
  3. }
  4. sum(1, 2, 3); // 返回 6
  5. function test(a, ...rest) {
  6.   console.log(a); // 获得传入的第一个参数,输出 '公众号'
  7.   console.log(rest); // 多余参数转为数组,输出 ['前端路引', '函数测试']
  8. }
  9. test('公众号', '前端路引', '函数测试');
复制代码
需特别注意,剩余参数只能放在最后,否则报语法错误 SyntaxError: Rest parameter must be last formal parameter。
比如:
  1. function test(...rest, a) { // 报错  SyntaxError: Rest parameter must be last formal parameter
  2. }
复制代码
解构赋值传参

还是 ES6 的花活之一,用于解构对象或数组参数。
  1. /**
  2. * 对象解构
  3. * name 为解构参数对象中的 name 属性
  4. * rest 为解构参数对象中剩余的属性,也是对象
  5. */
  6. function test1({ name, ...rest }) {
  7.   console.log(name); // 输出 '前端路引'
  8.   // rest 获得剩余为分配的对象属性
  9.   console.log(rest); // 输出 {age: 1}
  10. }
  11. test1({ name: '前端路引', age: 1 });
  12. /**
  13. * 数组解构
  14. * first 为第一个数组值
  15. * rest 为数组剩余值,也是一个数组
  16. */
  17. function test2([first, ...rest]) {
  18.   console.log(first); // 输出 '前端路引'
  19.   console.log(rest); // 输出 [1, '微信公众号']
  20. }
  21. test2(['前端路引', 1, '微信公众号']);
复制代码
参数使用使用解构赋值时,需特别注意,如果参数没传入参数,那么解构将会报错:
  1. // 报错 TypeError: Cannot destructure property 'name' of 'undefined' as it is undefined.
  2. function test({ name }) {
  3. }
  4. test();
复制代码
原因是未传入参数时,默认便是 undefined,对 undefined 解构便会报错!!这时候可以使用函数默认参数进行解决:
  1. // 表示未传入参数时使用空对象进行解构
  2. function test({ name } = {}) {}
  3. test();
复制代码
解构中也可以使用默认值:
  1. // 对象解构默认值
  2. function test1({ name = '前端路引' } = {}) {
  3.   console.log(name);
  4. }
  5. test1(); // 输出 '前端路引'
  6. test1({age: 1}); // 输出 '前端路引'
  7. // 需注意另一种写法
  8. function test2({ name } = { name: '前端路引' }) {
  9.   console.log(name);
  10. }
  11. test2(); // 输出 '前端路引'
  12. test2({age: 1}); // 输出 undefined
  13. // 数组解构默认值
  14. function test3([first = '前端路引', ...rest] = []) {
  15.   console.log(first);
  16. }
  17. test3();
复制代码
test2 中使用了一个默认对象,这个对象中有 name 属性,如果传入参数不存在时候,将会获得 name 属性值,但如果传入参数存在时,并且传入的对象中没有 name 属性,那么就只能是 undefined。
arguments 对象

使用 function 声明的函数,可以使用 arguments 所有参数。需注意 arguments 在箭头函数中不可用
arguments 是类数组对象,不是所有数组方法都可以使用,可以使用 ... 展开运算符转换为数组。
  1. function test() {
  2.   // arguments.push('微信公众号'); // 报错 TypeError: arguments.push is not a function
  3.   const temp = [...arguments]; // 使用展开运算符转换为数组
  4.   temp.push('微信公众号'); // 转为数组之后可以使用 push 方法
  5.   console.log(temp);
  6. }
  7. test(1, '前端路引', true); // 输出 [1, '前端路引', true, '微信公众号']
复制代码
函数作为参数

虽然 回调函数 参数这种方式已经被 Promise 代替,但如果要实现各种钩子函数,还是只能使用 function 作为参数传递。
使用函数作为参数的函数有一个专用名词叫做 高阶函数。多用于 事件处理、异步操作 等等。
  1. function test1(url, callback) {
  2.   // 模拟异步操作
  3.   setTimeout(() => {
  4.     callback({ data: '前端路引' });
  5.   }, 1000);
  6. }
  7. test1('/api', (response) => {
  8.   console.log(response.data); // 输出 '前端路引'
  9. });
  10. // 使用 Promise 改写 test1 函数:
  11. function test2(url) {
  12.   return new Promise((resolve, reject) => {
  13.     setTimeout(() => {
  14.       resolve({ data: '前端路引' });
  15.     }, 1000)
  16.   })
  17. }
  18. test2('/api').then((response) => {
  19.   console.log(response.data); // 输出 '前端路引'
  20. })
复制代码
当需要钩子函数时候,便无法使用 Promise 替换了,比如:
  1. function test({
  2.   url,
  3.   before, // 钩子函数,在请求开始时调用
  4.   callback,
  5. } = {}) {
  6.   before && before('请求开始');
  7.   setTimeout(() => {
  8.     // 执行完之后回调
  9.     callback && callback({ data: '前端路引' });
  10.   }, 1000)
  11. }
  12. test({
  13.   url: '/api',
  14.   before(msg) {
  15.     console.log(msg);
  16.   },
  17.   callback(response) {
  18.     console.log(response.data);
  19.   }
  20. })
复制代码
函数柯里化

通过闭包返回函数,分步传递参数,这种方式称为 函数柯里化。
  1. function test(a) {
  2.   return (b) => a * b; // 函数返回值是一个函数,用于二次调用
  3. }
  4. const double = test(2); // 第一次传入参数,获得一个返回函数
  5. console.log(double(5)); // 第二次传入参数,获得结果,输出 10
复制代码
由于闭包中的变量一直在内存中,所以在使用时候需注意内存泄漏问题!!
bind 方法绑定参数

bind 这方法不仅可以绑定内部 this 指针,还能用于固定部分参数,生成新函数。
  1. function test1(type, name) {
  2.   console.log(this);
  3.   return `${type}:${name}`;
  4. }
  5. const test2 = test1.bind({age: 1}, '微信公众号');
  6. console.log(test2('前端路引')); // 输出 '微信公众号:前端路引'
复制代码
test1.bind({age: 1}, '微信公众号') 作用是给 test1 绑定 this 指向 {age: 1} 对象,同时固定了第一个参数为 '微信公众号',返回一个新的函数,此函数只有剩下的 name 参数。
也可以使用 test1.bind(null, '微信公众号') 不绑定 this 指针,仅固定第一个参数。
隐式参数类型转换

由于 JS 的参数灵活性,在使用时,需特别注意类型转换问题。比如字符串 '5' 转为数字 5:
  1. function test(a, b) {
  2.   return a + b;
  3. }
  4. test('3', 5); // 返回 '35'(字符串拼接)
  5. test(3, '5'); // 返回 '35'
  6. test(3, 5); // 返回 8
复制代码
如果无法确定传入的参数类型,那么就有必要显式转换类型(如 Number(param)):
  1. function test(a, b) {
  2.   return Number(a) + Number(b);
  3. }
  4. test('3', 5); // 返回 8
  5. test(3, '5'); // 返回 8
  6. test(3, 5); // 返回 8
复制代码
或进行参数类型校验,当输入不合法时候,抛出异常:
  1. function test(a, b) {
  2.   if (typeof a !== 'number' || typeof b !== 'number') {
  3.     throw new Error('参数类型错误');
  4.   }
  5.   return a + b;
  6. }
  7. test('3', 5); // 抛出异常
复制代码
写在最后

理解各种函数传参方式,灵活运用可以在编程中玩出花来。各种优雅的设计模式、易于维护的高级代码,都离不开函数的使用技巧~~
在使用函数参数时,也需特别注意参数合法性校验,尤其是提供给外部调用的函数,必须做参数类型校验,避免程序出现参数类型错误。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册