一、深拷贝&浅拷贝
深拷贝:拷贝的是值,会重新开辟一片内存空间
浅拷贝:拷贝的是引用,会指向同一片内存空间
对于JS来说,简单数据类型默认是拷贝值(深拷贝),复杂数据类型默认是拷贝引用(浅拷贝),使用时一般要注意复杂类型的深拷贝实现方式。
二、JS中实现深拷贝的几种常见方式
通过JSON对象
缺点:无法拷贝对象里的函数;对于数组undefined会变成null
let objClone = JSON.parse(JSON.stringify(obj))
lodash库里的_.cloneDeep()
var _ = require('lodash');
var obj = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var objClone = _.cloneDeep(obj);
console.log(obj.b.f === objClone.b.f); // false
jQuery里的$.extend(true, objClone, obj)
// 深拷贝
var objClone = {}
var obj = {person: {name: 'xiaoming'}}
$.extend(true, objClone, obj)
objClone.name = 'hong'
alert(obj.person.name) // xiaoming
ES6对象展开运算符
缺点:只能实现第一层的拷贝,实际上不是真正的深拷贝
let objClone = {...obj}
对于数组,可使用slice、concat等
缺点:只能实现第一层的拷贝,实际上不是真正的深拷贝
Object.assign()拷贝
缺点:只能实现第一层的拷贝,实际上不是真正的深拷贝
手写实现深拷贝
简易版
// 深拷贝: 简易版-仅支持数组和普通对象
function deepClone(obj) {
if(typeof obj === 'object') {
let newObj = Array.isArray(obj)? [] : {}
for(let key in obj) {
// 避免相互引用出现死循环导致爆栈
if(obj === obj[key]) {
continue
}
if(obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key])
}
}
return newObj
} else {
return obj
}
}
精细版
// 深拷贝: 精细版-支持数组、对象、日期、Map、Set等
function checkType(any) {
return Object.prototype.toString.call(any).slice(8, -1)
}
function deepClone(any) {
if(checkType(any) === 'Object') {
let newObj = {}
for(let key in any) {
newObj[key] = deepClone(any[key])
}
return newObj
}
if(checkType(any) === 'Array') {
var arr = []
for(let i=0; i<any.length; i++) {
arr[i] = deepClone(any[i])
}
return arr
}
if(checkType(any) === 'Function') {
return new Function('return ' + any.toString()).call(this)
}
if(checkType(any) === 'Date') {
return new Date(any.valueOf())
}
if(checkType(any) === 'RegExp') {
return new Regexp(any)
}
if(checkType(any) === 'Map') {
let m = new Map()
for(let [k, v] of any) {
m.set(k, deepClone(v))
}
return m
}
if(checkType(any) === 'Set') {
let s = new Set()
for(let val of any) {
s.add(val)
}
return s
}
return any
}