展开操作符(...)会把数组或对象的元素“拷贝一份”放进新的数组或对象里,
原来的数组 / 对象本身不会被修改。
但拷贝是“浅拷贝”,不是深拷贝。

二、这段代码做了什么

1
2
3
4
let first = [1, 2]
let second = [3, 4]

let bothPlus = [0, ...first, ...second, 5]

等价理解为:

1
let bothPlus = [0, 1, 2, 3, 4, 5]

关键点

  • bothPlus一个全新的数组
  • firstsecond 没有被修改
1
2
first  // [1, 2]
second // [3, 4]

三、浅拷贝

1️⃣ 对基本类型(number / string)

1
2
3
4
5
6
let a = [1, 2]
let b = [...a]

b[0] = 100

a // [1, 2] ← 不受影响

因为:

  • 数字是值类型
  • 拷贝的是真实的值

2️⃣ 对引用类型(对象 / 数组)

1
2
3
4
5
6
let a = [{ x: 1 }]
let b = [...a]

b[0].x = 999

a // [{ x: 999 }] ← 被影响了

原因:

  • 展开只拷贝 第一层
  • 内部对象 仍然是同一个引用

这就是 浅拷贝 的准确含义。

四、“与解构相反”

操作 做的事
解构 从数组 / 对象里 取值出来
展开 把数组 / 对象里的值 铺开塞进去

解构

1
const [a, b] = [1, 2]

展开

1
const arr = [...[1, 2]]

一个是“拆”,一个是“铺”。

五、对象展开也是同一套规则

1
2
3
4
const obj1 = { a: 1 }
const obj2 = { b: 2 }

const merged = { ...obj1, ...obj2 }
  • merged 是新对象
  • obj1 / obj2 不变
  • 浅拷贝
1
2
3
4
5
const obj1 = { a: { x: 1 } }
const obj2 = { ...obj1 }

obj2.a.x = 2
obj1.a.x // 2

展开对象

1
2
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };

food属性会重写

对象展开还有其它一些限制。
它仅包含对象 自身的可枚举属性。
当展开一个对象实例时,会丢失其方法;

六、总结

展开操作符会创建一个“新的容器”,
但容器里的引用元素仍然指向原来的对象,
所以叫“浅拷贝”,原数组 / 对象本身不会被修改