Rest 参数:处理函数不定数量参数的优雅方式

Rest 参数:函数界的“百宝箱”,让你代码优雅起飞!

各位看官,咱们今天聊聊 JavaScript 里一个挺有意思的小家伙,它叫 Rest 参数,也有些地方喜欢叫它剩余参数。这家伙用好了,能让你的函数代码瞬间变得优雅起来,就像给代码穿了件高定礼服,瞬间提升了几个档次。

你有没有遇到过这种情况:写一个函数,但是你不知道调用的时候会传进来多少个参数?就像你开了一家小卖部,每天来买东西的顾客数量都不一样,有时候只有一个老头,有时候却挤满了放学的小学生。如果你的函数也面临这种“顾客盈门”的情况,Rest 参数就是你的救星!

一、什么是 Rest 参数?它能干啥?

简单来说,Rest 参数就是一种可以把函数调用时传入的“剩余”参数打包成一个数组的语法。它长这样:...参数名。 注意,那三个小点点 ... 可不是省略号,它是 Rest 参数的标志。

举个栗子:

function sum(a, b, ...numbers) {
  let total = a + b;
  for (let number of numbers) {
    total += number;
  }
  return total;
}

console.log(sum(1, 2, 3, 4, 5)); // 输出:15
console.log(sum(10, 20)); // 输出:30
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // 输出:55

在这个例子里,ab 是普通的参数,而 ...numbers 就是 Rest 参数。当我们调用 sum 函数时,前两个参数会分别赋值给 ab,剩下的所有参数都会被打包成一个名为 numbers 的数组。

看到了吗?有了 Rest 参数,我们的 sum 函数就可以接受任意数量的参数进行求和,简直不要太方便!就像一个百宝箱,什么都能往里装。

二、Rest 参数的“规矩”:也不是什么都能装!

Rest 参数虽然好用,但它也有自己的“规矩”,不是什么东西都能往里塞的。

  1. 只能是最后一个参数: Rest 参数必须是函数定义中的最后一个参数。就好比你排队买奶茶,Rest 参数只能站在队伍的最后面,不能插队!

    // 错误示例:Rest 参数不在最后
    function wrongExample(...numbers, a, b) {
      // 报错:Rest parameter must be last formal parameter
    }
    
    // 正确示例:Rest 参数在最后
    function correctExample(a, b, ...numbers) {
      console.log(a, b, numbers);
    }
  2. 只能有一个 Rest 参数: 一个函数定义中只能有一个 Rest 参数。就像你家里只能有一个储物间,不能再多了。

    // 错误示例:有两个 Rest 参数
    function anotherWrongExample(...numbers1, ...numbers2) {
      // 报错:Rest parameter must be last formal parameter
    }
  3. 不能用于 setter 函数: setter 函数是用来设置对象属性值的,它只能接受一个参数,所以不能使用 Rest 参数。就好比你家的门卫,一次只能放一个人进去,不能一下子放一群人。

    const obj = {
      set values(...args) { // 报错:A 'set' accessor cannot have rest parameter
        // ...
      }
    };

三、Rest 参数 vs arguments 对象:傻傻分不清楚?

在 ES6 之前,如果我们想要获取函数的所有参数,通常会使用 arguments 对象。它是一个类数组对象,包含了函数调用时传入的所有参数。

那么问题来了,既然有 arguments 对象了,为什么还需要 Rest 参数呢?它们有什么区别?

  • arguments 是一个类数组对象,而 Rest 参数是一个真正的数组。 这意味着你可以直接使用数组的方法,比如 mapfilterreduce 等,来操作 Rest 参数,而 arguments 对象则需要先转换成数组才能使用这些方法。

    function demo(a, b, ...numbers) {
      // numbers 是一个数组,可以直接使用数组方法
      numbers.forEach(number => console.log(number));
    
      // arguments 是一个类数组对象,需要先转换成数组才能使用数组方法
      // Array.from(arguments).forEach(arg => console.log(arg));
      // 或者 [...arguments].forEach(arg => console.log(arg));
    }
    
    demo(1, 2, 3, 4, 5);
  • arguments 对象包含了函数的所有参数,包括具名参数,而 Rest 参数只包含剩余的参数。 也就是说,如果函数定义了具名参数,那么 arguments 对象会包含这些参数,而 Rest 参数只会包含那些没有被具名参数接收的参数。

    function demo(a, b, ...numbers) {
      console.log("a:", a);
      console.log("b:", b);
      console.log("numbers:", numbers);
      console.log("arguments:", arguments);
    }
    
    demo(1, 2, 3, 4, 5);
    // 输出:
    // a: 1
    // b: 2
    // numbers: [3, 4, 5]
    // arguments: [1, 2, 3, 4, 5]
  • arguments 对象在严格模式下会有一些限制,而 Rest 参数没有这些限制。 在严格模式下,arguments 对象会被冻结,不能被修改。

总而言之,Rest 参数比 arguments 对象更加灵活、方便,也更加符合现代 JavaScript 的编程风格。所以,在 ES6 之后,我们应该尽量使用 Rest 参数来代替 arguments 对象。

四、Rest 参数的妙用:让你的代码更上一层楼!

Rest 参数的应用场景非常广泛,可以用来解决各种各样的问题。下面我们来看几个具体的例子,感受一下 Rest 参数的魅力。

  1. 模拟 console.log console.log 函数可以接受任意数量的参数,并将它们打印到控制台上。我们可以使用 Rest 参数来模拟 console.log 的功能。

    function myLog(...args) {
      let output = "";
      for (let arg of args) {
        output += arg + " ";
      }
      console.log(output);
    }
    
    myLog("Hello", "world", "!"); // 输出:Hello world !
  2. 实现一个简单的平均数函数: 我们可以使用 Rest 参数来实现一个可以计算任意数量数字的平均数的函数。

    function average(...numbers) {
      if (numbers.length === 0) {
        return 0;
      }
      let sum = 0;
      for (let number of numbers) {
        sum += number;
      }
      return sum / numbers.length;
    }
    
    console.log(average(1, 2, 3, 4, 5)); // 输出:3
    console.log(average(10, 20, 30)); // 输出:20
    console.log(average()); // 输出:0
  3. 结合解构赋值使用: Rest 参数可以和解构赋值一起使用,让代码更加简洁易懂。

    function processData(first, second, ...rest) {
      console.log("First:", first);
      console.log("Second:", second);
      console.log("Rest:", rest);
    }
    
    const data = [1, 2, 3, 4, 5];
    processData(...data); // 使用展开运算符将数组传递给函数
    // 输出:
    // First: 1
    // Second: 2
    // Rest: [3, 4, 5]

    在这个例子中,我们使用了展开运算符 ... 将数组 data 传递给 processData 函数。processData 函数使用 Rest 参数接收剩余的参数,并将它们打印到控制台上。

  4. 结合高阶函数使用: Rest 参数在高阶函数中也很有用。比如,我们可以创建一个通用的函数来包装另一个函数,并记录它的调用次数。

function withCounter(fn) {
  let count = 0;
  return function(...args) {
    count++;
    console.log(`Function called ${count} times`);
    return fn(...args); // 将所有参数传递给原始函数
  };
}

function add(x, y) {
  return x + y;
}

const countedAdd = withCounter(add);

console.log(countedAdd(2, 3)); // 输出:Function called 1 times  5
console.log(countedAdd(5, 10)); // 输出:Function called 2 times 15
console.log(countedAdd(1, 1)); // 输出:Function called 3 times 2

在这个例子中,withCounter 函数接受一个函数 fn 作为参数,并返回一个新的函数。新的函数内部维护一个计数器 count,每次调用时都会增加计数器的值,并打印调用次数。然后,它使用展开运算符 ...args 将所有参数传递给原始函数 fn

五、总结:Rest 参数,代码优雅的秘密武器!

总而言之,Rest 参数是 JavaScript 中一个非常实用的语法特性。它可以让你编写更加灵活、简洁、易懂的函数代码。掌握了 Rest 参数,就好像拥有了一件秘密武器,可以让你在代码的世界里披荆斩棘,一路高歌猛进。

下次当你需要编写一个可以接受不定数量参数的函数时,不妨试试 Rest 参数吧!相信它会给你带来意想不到的惊喜。记住,代码就像艺术品,优雅的代码更赏心悦目!

希望这篇文章能帮助你更好地理解和使用 Rest 参数。如果你觉得这篇文章对你有帮助,不妨点个赞,或者分享给你的朋友们。让我们一起学习,一起进步,一起成为更优秀的程序员!

最后,祝大家编程愉快,代码无 bug!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注