浅拷贝
创建一个对象的复制,但是其中的元素都是原始对象的引用,所以只解决了第一层的拷贝。如果原始对象的元素变化,在浅拷贝中也会体现这些变化。
实现方式
前2种是同时支持对象和数组的拷贝:
实现方式1 (Object 方法)
1 2
| const b = Object.assign({}, a); const c = Object.create(a);
|
实现方式2 (展开运算符)
1 2
| const b = { ...a }; const c = [...a];
|
以下是只针对数组的拷贝:
实现方式3 (Array.prototype.concat())
实现方式4 (Array.prototype.slice())
实现方式5 (Array.from())
1
| const b = Array.from(a);
|
深拷贝
创建一个对象的全新复制,会递归地复制对象的所有元素和子元素。如果原始对象的元素变化,深拷贝创建的对象不受影响。
实现方式
实现方式1 (JSON 序列化):
1
| const b = JSON.parse(JSON.stringify(a));
|
上述方法的局限性:
- 会忽略值为 undefined、symbol 或函数的属性
- 不支持循环引用,会直接报错
实现方式2 (2022年新特性,core-js 已支持该 polyfill):
1 2 3 4 5 6
| const b = window.structuredClone(a);
const arr = new Uint8Array(); const c = window.structuredClone(arr, { transfer: [arr.buffer] })
|
- 支持循环引用
- 还是不支持序列化,含有值为 undefined、symbol 或函数的属性,会直接报错
实现方式3 (MessageChannel):
1 2 3 4 5 6 7
| const mySturcturedClone = (obj) => new Promise((resolve) => { const { port1, port2} = new MessageChannel(); port2.onmessage = (ev) => resolve(ev.data); port1.postMessage(obj); });
const b = await mySturcturedClone(a);
|
- 支持值为 undefined 的属性
- 支持循环引用
- 还是不支持 symbol 或函数的属性,会直接报错
实现方式4 (递归遍历):
```javascript
const myStructuredClone = (origin) => {
const map = new WeakMap(); // 因为对象的 key 可能是对象,所以使用 WeakMap
const deepClone = (obj) => {
if (typeof obj !== ‘object’ || obj === null) return obj; // 基本类型直接返回
if (map.has(obj)) return map.get(obj); // 防止循环引用
const clone = Array.isArray(obj) ? [] : {}; // 判断是数组还是对象
map.set(obj, clone);
for (const key in obj) { // 同时可以遍历数组和对象,数组的 key 为索引
// Object 是一个构造函数,所以要取 Object.prototype 对象上的方法
if (Object.prototype.hasOwnProperty.call(obj, key)) { // 判断是否是自身属性,而不是原型链上的属性
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
return deepClone(origin);
}