阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > rest参数语法

rest参数语法

作者:陈川 阅读数:59957人阅读 分类: JavaScript

rest参数的基本概念

ECMAScript 6引入的rest参数语法允许我们将不定数量的参数表示为一个数组。这种语法通过在函数参数前添加三个点(...)来实现,它解决了ES5中处理可变参数时需要使用arguments对象的繁琐问题。

function sum(...numbers) {
  return numbers.reduce((prev, curr) => prev + curr, 0);
}

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

rest参数与arguments对象的区别

rest参数与传统的arguments对象有几个关键区别:

  1. rest参数是真正的数组实例,可以直接使用数组方法
  2. arguments对象包含所有参数,而rest参数只包含没有对应形参的剩余参数
  3. rest参数必须在参数列表的最后
// 使用arguments对象
function oldWay() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('-');
}

// 使用rest参数
function newWay(...args) {
  return args.join('-');
}

console.log(oldWay('a', 'b', 'c')); // "a-b-c"
console.log(newWay('a', 'b', 'c')); // "a-b-c"

rest参数的典型应用场景

函数参数收集

rest参数最常见的用途是收集函数的多余参数:

function logWithPrefix(prefix, ...messages) {
  messages.forEach(message => {
    console.log(`${prefix}: ${message}`);
  });
}

logWithPrefix('INFO', '系统启动', '加载配置完成', '服务已就绪');

替代apply方法

在ES5中,我们需要使用apply来将数组展开为参数,现在可以使用rest参数和扩展运算符:

// ES5方式
Math.max.apply(null, [1, 2, 3]);

// ES6方式
Math.max(...[1, 2, 3]);

// 结合rest参数
function wrapper(...args) {
  return Math.max(...args);
}

解构赋值中的rest参数

rest参数语法也可以用于解构赋值:

const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]

const {a, b, ...others} = {a: 1, b: 2, c: 3, d: 4};
console.log(a); // 1
console.log(b); // 2
console.log(others); // {c: 3, d: 4}

rest参数的注意事项

  1. rest参数必须是函数的最后一个参数:
// 错误示例
function invalid(a, ...b, c) {
  // SyntaxError: Rest parameter must be last formal parameter
}
  1. rest参数不计入函数的length属性:
function func1(a, b) {}
function func2(a, b, ...c) {}

console.log(func1.length); // 2
console.log(func2.length); // 2
  1. 箭头函数也可以使用rest参数:
const sum = (...nums) => nums.reduce((a, b) => a + b, 0);

rest参数的高级用法

类型检查和参数验证

可以利用rest参数进行参数类型检查:

function checkTypes(...args) {
  const types = args.map(arg => typeof arg);
  console.log('参数类型:', types);
}

checkTypes(1, 'text', true, {}, []); 
// 输出: ["number", "string", "boolean", "object", "object"]

实现函数组合

rest参数可以方便地实现函数组合:

function compose(...fns) {
  return x => fns.reduceRight((v, f) => f(v), x);
}

const add5 = x => x + 5;
const multiply3 = x => x * 3;
const square = x => x * x;

const transform = compose(square, multiply3, add5);
console.log(transform(2)); // ((2 + 5) * 3)^2 = 441

创建可变参数的构造函数

在类中使用rest参数:

class Point {
  constructor(...coords) {
    this.coords = coords;
  }
  
  get dimensions() {
    return this.coords.length;
  }
}

const point2D = new Point(10, 20);
const point3D = new Point(10, 20, 30);

console.log(point2D.dimensions); // 2
console.log(point3D.dimensions); // 3

rest参数与扩展运算符的区别

虽然rest参数和扩展运算符都使用三个点(...)语法,但它们的作用相反:

  • rest参数将多个参数收集到一个数组中
  • 扩展运算符将数组或对象展开为多个元素
// rest参数:收集
function collect(...items) {
  console.log(items);
}

// 扩展运算符:展开
const numbers = [1, 2, 3];
console.log(...numbers); // 等价于 console.log(1, 2, 3)

实际项目中的应用示例

实现一个简单的日志记录器

class Logger {
  constructor(prefix = 'LOG') {
    this.prefix = prefix;
  }
  
  log(...messages) {
    const timestamp = new Date().toISOString();
    console.log(`[${timestamp}] ${this.prefix}:`, ...messages);
  }
}

const appLogger = new Logger('APP');
appLogger.log('用户登录', {username: 'admin'});
appLogger.log('数据加载完成', '耗时200ms');

创建灵活的API请求函数

async function apiRequest(method, endpoint, ...params) {
  let options = {};
  
  if (method === 'GET') {
    endpoint += '?' + new URLSearchParams(params[0]).toString();
  } else {
    options = {
      method,
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify(params[0] || {})
    };
  }
  
  const response = await fetch(endpoint, options);
  return response.json();
}

// 使用示例
apiRequest('GET', '/api/users', {page: 1, limit: 10})
  .then(data => console.log(data));

apiRequest('POST', '/api/users', {name: 'John', age: 30})
  .then(data => console.log(data));

性能考虑

虽然rest参数提供了便利,但在性能敏感的场景需要注意:

  1. 每次函数调用都会创建一个新的数组实例
  2. 对于高频调用的函数,可能要考虑其他实现方式
  3. 在大多数应用场景中,这种性能开销可以忽略不计
// 性能测试示例
function testPerformance(fn, iterations = 1000000) {
  const start = performance.now();
  for (let i = 0; i < iterations; i++) {
    fn(1, 2, 3, 4, 5);
  }
  return performance.now() - start;
}

function withRest(...args) {
  return args.length;
}

function withoutRest(a, b, c, d, e) {
  return arguments.length;
}

console.log('使用rest参数:', testPerformance(withRest), 'ms');
console.log('不使用rest参数:', testPerformance(withoutRest), 'ms');

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌