一、Object.assign(target, source)
基本数据类型的拷贝,是深拷贝;引用数据类型的拷贝,是浅拷贝
①对象复制
var source = {
name: '库里',
player: {
num: 30,
age: 34
}
}
var target = {}
target = Object.assign(target, source)
target.name = 'curry' // 基本类型
target.player.num = 3 // 引用类型
console.log('source:', source) // { name: '库里', player: { num: 3, age: 34 } }
console.log('target:', target) // { name: 'curry', player: { num: 3, age: 34 } }
②数组复制
var source = [
'库里',
{
num: 30,
age: 34
}
]
var target = []
target = Object.assign(target, source)
target[0] = 'curry' // 基本类型
target[1].num = 3 // 引用类型
console.log('source:', source) // [ '库里', { num: 3, age: 34 } ]
console.log('target:', target) // [ 'curry', { num: 3, age: 34 } ]
二、扩展运算符(…)
基本数据类型的拷贝,是深拷贝;引用数据类型的拷贝,是浅拷贝
①对象复制
var source = {
name: '库里',
player: {
num: 30,
age: 34
}
}
var target = { ...source }
target.name = 'curry' // 基本类型
target.player.num = 3 // 引用类型
console.log('source:', source) // { name: '库里', player: { num: 3, age: 34 } }
console.log('target:', target) // { name: 'curry', player: { num: 3, age: 34 } }
②数组复制
var source = [
'库里',
{
num: 30,
age: 34
}
]
var target = [ ...source ]
target[0] = 'curry' // 基本类型
target[1].num = 3 // 引用类型
console.log('source:', source) // [ '库里', { num: 3, age: 34 } ]
console.log('target:', target) // [ 'curry', { num: 3, age: 34 } ]
三、**var target = JSON.parse(JSON.stringify(source))**
无论是基本数据类型还是引用数据类型,都是深拷贝,但有些问题存在!
JSON.stringify():将 JavaScript值(通常为对象或数组)转换为 JSON 字符串
JSON.parse():将一个 JSON 字符串转换为对象或数组
①对象深复制
var source = {
name: '库里',
player: {
num: 30,
age: 34
}
}
var target = JSON.parse(JSON.stringify(source))
target.name = 'curry' // 基本类型
target.player.num = 3 // 引用类型
console.log('source:', source) // { name: '库里', player: { num: 30, age: 34 } }
console.log('target:', target) // { name: 'curry', player: { num: 3, age: 34 } }
②数组深复制
var source = [
'库里',
{
num: 30,
age: 34
}
]
var target = JSON.parse(JSON.stringify(source))
target[0] = 'curry' // 基本类型
target[1].num = 3 // 引用类型
console.log('source:', source) // [ '库里', { num: 30, age: 34 } ]
console.log('target:', target) // [ 'curry', { num: 3, age: 34 } ]
四、**var target = JSON.parse(JSON.stringify(source))深拷贝存在的问题:**
- 对象中有Date类型时,序列化之后会变成字符串类型。
- 对象中有Error或RegExp类型时,序列化之后会变成空对象{}
- 对象中有undefined和Function类型数据的时候,序列化之后会直接丢失。
- 对象中有NaN、Infinity和-Infinity的时候,序列化之后value会变成null。
- 对象循环引用后,执行序列化会报错。
- 最后,深拷贝建议使用递归,安全方便。
// JSON.parse(JSON.stringify())深拷贝存在的问题
let source = {
name: '库里',
player: {
num: 30,
age: 34
},
date: new Date(), // 深拷贝后类型会变为string
reg: /^[1-9]$/, // 或new RegExp('[1-9]'),深拷贝后会变为空对象{}
err: new Error('err'), // 深拷贝后会变为空对象{}
undef: undefined, // 深拷贝后会丢失
func: () => { console.log('func') }, // 深拷贝后会丢失
nan: NaN, // 深拷贝后会变成null
infinityMax: Infinity, // 深拷贝后会变成null
infinityMin: -Infinity // 深拷贝后会变成null
}
// source.child = source // 循环引用后,深拷贝报错:TypeError: Converting circular structure to JSON
const target = JSON.parse(JSON.stringify(source))
console.log('type:', typeof source.date) // object
console.log('type:', typeof target.date) // string
console.log('source:', source)
console.log('target:', target)
五、 递归深拷贝(推荐)
function deepClone (target, wm = new WeakMap()) {
if (target === null) return target // null 直接返回
if (target instanceof Date) return new Date(target)
if (target instanceof RegExp) return new RegExp(target)
// 可能是对象或者普通的值,如果是函数的不需要深拷贝
if (typeof target !== 'object') { // 包括: boolean | number | string | undefined | function
return target
}
// 防止循环引用
if (wm.get(target)) {
return wm.get(target)
}
// 实例的构造函数,就是其类原型的构造函数constructor()方法,类原型的构造函数constructor,直接指向类本身
// obj.constructor === Object.prototype.constructor === Object
let cloneTarget = new target.constructor()
wm.set(target, cloneTarget)
for (let key in target) {
cloneTarget[key] = deepClone(target[key], wm)
}
return cloneTarget
}
let source = {
name: '库里',
player: {
num: 30,
age: 34
},
date: new Date(),
reg: /^[1-9]$/,
err: new Error('err'),
undef: undefined,
func: () => { console.log('func') },
nan: NaN,
infinityMax: Infinity,
infinityMin: -Infinity
}
source.self = source // 循环引用,即对象的属性间接或直接的引用了自身的情况
let target = deepClone(source)
console.log('source:', source)
console.log('target:', target)