JS `Rest Parameters` (`…`):捕获函数参数为数组

各位观众老爷,大家好!今天咱们来聊聊 JavaScript 里一个相当实用,但又容易被忽略的小可爱——Rest Parameters(剩余参数)。这玩意儿就像一个神奇的口袋,能把函数接收到的零散参数打包成一个数组,简直是懒人福音,代码简化神器!

一、什么是 Rest Parameters?

简单来说,Rest Parameters 允许我们将一个不定数量的参数表示为一个数组。它的语法形式是 ...参数名,必须是函数参数列表的最后一个参数。

举个例子:

function sum(a, b, ...numbers) {
  console.log("a:", a);
  console.log("b:", b);
  console.log("numbers:", numbers);
}

sum(1, 2, 3, 4, 5);
// 输出:
// a: 1
// b: 2
// numbers: [3, 4, 5]

在这个例子中,ab 分别接收了前两个参数,而 ...numbers 则把剩下的所有参数打包成了一个名为 numbers 的数组。

重点:

  • Rest Parameters 只能是最后一个参数。
  • 一个函数只能有一个 Rest Parameter。
  • Rest Parameter 接收的是剩余的 所有 参数。
  • Rest Parameter 得到的是一个真正的数组,可以使用数组的所有方法。

二、Rest Parameters 与 arguments 对象的区别

在 Rest Parameters 出现之前,JavaScript 提供了一个名为 arguments 的对象,它也包含了函数接收到的所有参数。但 arguments 对象并不是一个真正的数组,而是一个类数组对象(array-like object)。这意味着它没有数组的 forEachmapfilter 等方法。

对比一下:

特性 arguments 对象 Rest Parameters
数据类型 类数组对象 (array-like object) 真正的数组 (Array)
拥有数组方法
是否可以 slice 可以,但返回的仍然是类数组对象 可以,返回的是数组
是否可以 forEach, map
位置限制 无,可以在任何位置访问 必须是最后一个参数
是否是 ES6 特性

看个例子:

function foo() {
  console.log("arguments:", arguments);
  // arguments.forEach(item => console.log(item)); // 报错:arguments.forEach is not a function
  console.log("arguments to array:", Array.prototype.slice.call(arguments));
}

foo(1, 2, 3);

function bar(...args) {
  console.log("args:", args);
  args.forEach(item => console.log(item)); // 正常输出 1, 2, 3
}

bar(1, 2, 3);

从上面的例子可以看出,arguments 对象使用起来比较麻烦,需要手动转换为数组才能使用数组的方法。而 Rest Parameters 则直接提供了一个真正的数组,使用起来更加方便。

三、Rest Parameters 的应用场景

Rest Parameters 在很多场景下都能大显身手,让我们的代码更加简洁优雅。

1. 求和函数:

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

console.log(sum(1, 2, 3)); // 输出:6
console.log(sum(1, 2, 3, 4, 5)); // 输出:15

2. 字符串拼接:

function concatStrings(separator, ...strings) {
  return strings.join(separator);
}

console.log(concatStrings(", ", "apple", "banana", "cherry")); // 输出:apple, banana, cherry
console.log(concatStrings(" - ", "one", "two", "three")); // 输出:one - two - three

3. 动态参数处理:

function processData(action, ...data) {
  switch (action) {
    case "add":
      console.log("Adding data:", data);
      // ... 添加数据的逻辑
      break;
    case "update":
      console.log("Updating data:", data);
      // ... 更新数据的逻辑
      break;
    case "delete":
      console.log("Deleting data:", data);
      // ... 删除数据的逻辑
      break;
    default:
      console.log("Invalid action");
  }
}

processData("add", { name: "John", age: 30 }, { city: "New York" });
processData("update", { id: 1, name: "Jane" });

4. 函数柯里化 (Currying) 时的参数收集:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return function(...nextArgs) {
        return curried(...args, ...nextArgs);
      }
    }
  }
}

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

const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)(3)); // Output: 6
console.log(curriedAdd(1, 2)(3)); // Output: 6
console.log(curriedAdd(1)(2, 3)); // Output: 6
console.log(curriedAdd(1, 2, 3));   // Output: 6

在这个柯里化的例子中, ...args...nextArgs 用于收集每次调用函数时传入的参数,直到参数的数量达到原始函数 add 的参数数量,才执行 add 函数。

5. 与解构赋值结合:

Rest Parameters 还可以与解构赋值结合使用,提取部分参数后,将剩余的参数打包成数组。

function processUserInfo(name, age, ...otherInfo) {
  console.log("Name:", name);
  console.log("Age:", age);
  console.log("Other Info:", otherInfo);
}

processUserInfo("Alice", 25, "Engineer", "New York", "Likes coding");
// 输出:
// Name: Alice
// Age: 25
// Other Info: ["Engineer", "New York", "Likes coding"]

function getFirstAndRest(first, ...rest) {
  return {
    first: first,
    rest: rest
  };
}

const { first, rest } = getFirstAndRest(1, 2, 3, 4, 5);
console.log("First:", first); // 输出:First: 1
console.log("Rest:", rest);   // 输出:Rest: [2, 3, 4, 5]

四、注意事项

  • 只能有一个 Rest Parameter: 一个函数只能定义一个 Rest Parameter。如果定义了多个,JavaScript 引擎会报错。
  • 必须是最后一个参数: Rest Parameter 必须是函数参数列表中的最后一个参数。如果它前面还有其他参数,Rest Parameter 才能正确地捕获剩余的参数。
  • 箭头函数: Rest Parameters 在箭头函数中同样适用。
const multiply = (...numbers) => numbers.reduce((acc, val) => acc * val, 1);

console.log(multiply(2, 3, 4)); // 输出:24
  • new 关键字一起使用: Rest Parameters 可以与 new 关键字一起使用,创建构造函数。
class Person {
  constructor(name, ...hobbies) {
    this.name = name;
    this.hobbies = hobbies;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I enjoy ${this.hobbies.join(", ")}.`);
  }
}

const john = new Person("John", "reading", "hiking", "coding");
john.greet(); // 输出:Hello, my name is John and I enjoy reading, hiking, coding.

五、总结

Rest Parameters 是 ES6 引入的一个非常方便的特性,它可以让我们更轻松地处理不定数量的函数参数。它不仅简化了代码,提高了可读性,还避免了使用 arguments 对象带来的麻烦。掌握 Rest Parameters,可以让你编写更加优雅、高效的 JavaScript 代码。

记住,Rest Parameters 就像一个万能口袋,可以把剩余的参数都装进去。下次当你需要处理不定数量的参数时,不妨试试它,你会发现它真的很好用!

好了,今天的讲座就到这里。希望大家能喜欢上这个小可爱——Rest Parameters! 谢谢大家!

发表回复

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