浅拷贝与深拷贝什么关系?
前言
实现深浅克隆是面试当中出现频率非常高的题目,既然经常考,那么自然也用很多地方会用到它,比如说后台返回的一个对象,前端要对其加工,然后用来给不同的变量赋值,假如这个对象很复杂,就很容易被相互污染(因为对象是引用类型),那么这个时候就需要一个拷贝对象的函数替我们分忧~
浅拷贝
对于一个对象,只克隆其中最外面一层,深层的对象依旧是通过引用指向同一块堆内存。
ES5实现方式
我们可以看到,当tar对象为a对象的浅拷贝,只拷贝一层内容,再下一层还是用过引用指向同一块堆内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function shallowClone(sourceObj) { if (!sourceObj || typeof sourceObj !== "object") { console.log("参数不为对象或数组"); return false; } var taegetObj = sourceObj.constructor === Array ? [] : {}; for (var keys in sourceObj) { if (sourceObj.hasOwnProperty(keys)) { taegetObj[keys] = sourceObj[keys]; } } return taegetObj; } var a = { name: "huajinbo", dream: { eat: ["banana", "apple", "tomato"], play: { game: "Fruit ninja", sport: "basketball" } } }; let tar = shallowClone(a); tar.dream.eat = ["pear"]; tar.name = 'vince' console.log(tar.name === a.name); console.log(tar.dream.eat === a.dream.eat);
|
ES6浅拷贝
这里我们用了ES6的结构赋值,快捷进行了浅拷贝,效果还是和上面的一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var a = { name: "huajinbo", dream: { eat: ["banana", "apple", "tomato"], play: { game: "Fruit ninja", sport: "basketball" } } };
let tar = {...a} tar.dream.eat = ["pear"]; tar.name = 'vince' console.log(tar.name === a.name); console.log(tar.dream.eat === a.dream.eat);
|
深拷贝
深拷贝意味着将整个对象完全拷贝,但是目前来说并没有完美的解决方案,但是对于很多情况来说,以下这种方案就能满足需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function deepClone (sourceObj) { if(!sourceObj || typeof sourceObj !== 'object') { console.log('非对象或数组') return } return JSON.parse(JSON.stringify(sourceObj)) } var a = { name: "huajinbo", dream: { eat: ["banana", "apple", "tomato"], play: { game: "Fruit ninja", sport: "basketball" } } };
let tar = deepClone(a) tar.dream.eat = ["pear"]; tar.name = 'vince' console.log(tar.name === a.name); console.log(tar.dream.eat === a.dream.eat);
|
虽然方法看上去简单粗暴,但是无论嵌套多深,这种方案总是能解决问题,似乎是个完美的解决方案。但是!当我们的对象中含有以下几类对象时:
- 对象中含有函数
- 对象中含有正则表达式
- 对象中含有稀疏数组
- 对象中含有构造函数
我们针对以上四个问题分别做测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function person(pname) { this.name = pname; }
const Messi = new person('Messi');
function say() { console.log('hi'); };
const oldObj = { a: say, b: new Array(1), c: new RegExp('ab+c', 'i'), d: Messi };
const newObj = JSON.parse(JSON.stringify(oldObj));
console.log(newObj.a, oldObj.a);
console.log(newObj.b[0], oldObj.b[0]);
console.log(newObj.c, oldObj.c);
console.log(newObj.d.constructor, oldObj.d.constructor);
|