在JavaScript分为两种原始值和引用值类型,原始值之间的复制是值对值得复制,而引用类型则是引用对引用的复制;
// 原始值的复制; let num1 = 1; let num2 = num1; num2++; console.log(num1,num2); // 1,2
// 引用类型的复制; let joe = { name:"joe", age:20 }
let john = joe; john.name = 'john';
console.log(john === joe,joe.name); // true john
如你所见,当我们把joe 赋值给john时实际上并没有完全复制一个新的,就好像是下面这样:
图片来自 “现代JavaScript教程” 网站;
Object.assign(target,arg1,arg2,...)方法用于将第二个参数及以后所有的对象都合并到第一个对象中,并返回第一个对象;
let user = { name:"申屠肆", hobbies:{ first:'JavaScript', second:'Node' } } let user_copy = Object.assign({job:'Web开发'},user,); user_copy.name = '谢必安'; user_copy.hobbies['third'] = 'Python'; console.log(user_copy.name,user.name); console.log(user_copy.hobbies,user.hobbies); //控制台 谢必安 申屠肆 { first: 'JavaScript', second: 'Node', third: 'Python' } { first: 'JavaScript', second: 'Node', third: 'Python' }
虽然Object.assign可以达到我们想要的效果,但是,可以发现,如果合并的对象的某个键对应的值仍然是对象,则还是会存下对象的引用;
接下来,让我们探寻更进一步的方案
// 采用递归 + JSON对象的方法 来完成深度复制 function deep_clone(source,target = {}) { for(let key in source) { if(source[key].constructor === Object) { target[key] = deep_clone(source[key]) }else if (source[key].constructor === Array) { target[key] = JSON.parse(JSON.stringify(source[key])) } else { target[key] = source[key]; } } return target; }
让我们来试一下
var ball = { name:'足球', shape:{ text:'圆形', size:40 }, abc:[1], def:[{user:'谢绝'}] } function deep_clone(source,target = {}) { for(let key in source) { if(source[key].constructor === Object) { target[key] = deep_clone(source[key]) }else if (source[key].constructor === Array) { target[key] = JSON.parse(JSON.stringify(source[key])) } else { target[key] = source[key]; } } return target; }
var ball_copy = deep_clone(ball); ball_copy.shape.text="方形"; ball_copy.abc.push(2); ball_copy.def[0].user = "谢绝-copy"; console.log(ball_copy,ball);
// 控制台打印 { name: '足球', shape: { text: '方形', size: 40 }, abc: [ 1, 2 ], def: [ { user: '谢绝-copy' } ] } { name: '足球', shape: { text: '圆形', size: 40 }, abc: [ 1 ], def: [ { user: '谢绝' } ] }
使用该方法需要注意的是,不能有“环形结构”