再来看看展开语法(...)

109 阅读3分钟

概念

我们先看看MDN上对(...)的解释:

spread (...)  语法允许迭代数组或字符串等可迭代字符串在预期有零个或更多参数(用于函数调用)或元素(用于数组字面量)的地方进行扩展。在对象字面量中,扩展语法枚举对象的属性,并将键值对添加到正在创建的对象中。

展开语法看起来与剩余参数语法一模一样。在某种程度上,扩展语法与剩余参数语法正好相反。扩展语法是将数组“扩展”为元素,而其余语法是将多个元素收集起来,然后“浓缩”为一个元素。

从概念中可以提炼出(...)的几个用法:

  • 第一句话的意思是:可以将 可迭代对象 进行展开;
  • 第二句话的意思是:可以将 对象 展开;
  • 第三句话的意思是:和 剩余参数 的用法一样,但是功能相反。

下面就逐个解释每个的用法,并且可以看出(...)带来的便利:

展开数组

  1. 结合两个数组
var parts = ["a", "b"];
var lyrics = ["c", ...parts, "d", "e"]; // ['c', 'a', 'b', 'd', 'e']
  1. 浅拷贝数组
const arr = [1,2,3];
const arr2 = [...arr];//[1,2,3]

//如果是ES5
const a1 = [1, 2, 3];
const a2 = a1.concat();
  1. 合并数组
const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
const arr3 = [...arr1, ...arr2];

//ES5
const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];
const arr3 = arr1.concat(arr2);
  1. 替代 apply
const arr = [1, 2, 3];
Math.max(...arr); // 相当于 Math.max(1, 2, 3)

//ES5
Math.max.apply(null, arr)

展开字符串

字符串也是可迭代对象

const str = "hello";
const chars = [...str]; // ["h", "e", "l", "l", "o"]

展开对象

在对象字面量中展开可枚举属性:

const obj = { a: 1, b: 2 };

// 1. 合并对象
const merged = { ...obj, c: 3 }; // { a: 1, b: 2, c: 3 }

// 2. 覆盖属性
const updated = { ...obj, a: 99 }; // { a: 99, b: 2 }

// 3. 浅拷贝对象
const copy = { ...obj }; // { a: 1, b: 2 }(新对象)

剩余参数(Rest Parameters)

概念:

剩余参数允许我们将一个不定数量的参数表示为一个数组。

  1. 函数参数收集
function sum(...theArgs) {
  let total = 0;
  for (const arg of theArgs) {
    total += arg;
  }
  return total;
}
  1. 数组和对象的解构赋值
//数组
const [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest);  // [2, 3, 4]

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

注意事项

  1. 浅拷贝问题 展开对象或数组时,只会复制第一层(深层次数据仍是引用):
const obj = { a: { b: 1 } };
const copy = { ...obj };
copy.a.b = 2; // 会影响原对象 obj.a.b
  1. 仅适用于可迭代对象 在数组或函数参数中使用时,只能用于可迭代对象
const obj = { key1: "value1" };
const array = [...obj]; // TypeError: obj is not iterable
  1. 剩余参数必须在最后
// ✅ 正确
function fn(a, ...rest) {}
const [x, ...others] = [1, 2, 3];

// ❌ 错误
function fn(...rest, a) {}
  1. 剩余参数和  arguments 对象的区别
  • 剩余参数只收集未被形参匹配的实参,arguments包含所有实参(无论是否有形参)。
// 剩余参数
function log(a, b, ...rest) {
  console.log(a, b, rest); // 1, 2, [3, 4]
}
log(1, 2, 3, 4); // rest 只收集未被 a、b 接收的 3 和 4

// arguments
function log(a, b) {
  console.log(arguments); // [1, 2, 3, 4](即使形参只有 a、b)
}
log(1, 2, 3, 4);
  • 剩余参数是真正的数组,而arguments包含为类数组,如果想使用数组方法,需要先转换成数组
function sum(...nums) {
  return nums.reduce((acc, num) => acc + num, 0);
}
sum(1, 2, 3); // 6

// arguments
function fn() {
  const args = Array.from(arguments); // 转为数组
  console.log(args.map(x => x * 2)); // [2, 4, 6]
}
fn(1, 2, 3);

一句话总结

  • Spread (...)展开数组、对象或字符串,用于函数调用、数组/对象构造。
  • Rest (...)收集剩余元素,用于函数参数或解构赋值。