Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
我们经常在操作数据的时候遇到数据拷贝问题,你知道浅拷贝与深拷贝的区别吗?日常中可以通过哪些方式来实现深浅拷贝的?你还能通过手写来实现他们吗?
考察点
- 对浅拷贝与深度拷贝的理解
- 项目开发数据操作技巧的经验
- for...in,hasOwnProperty,数据类型以及递归的使用状况
解题思路
let pokemon = {
name: "皮卡丘",
hp: 48,
skill: {
a: {
name: "电光一闪",
power: 40
},
b: {
name: "十万伏特",
power: 90
},
c: null,
d: null
}
}
// 浅拷贝
let obj1 = Object.assign(pokemon)
obj1.skill.a.power = 0;
console.log(pokemon.skill.a.power) // 0
console.log(pokemon.skill == obj1.skill) // true
// 深拷贝
let obj2 = deepClone(pokemon)
obj2.skill.b.power = 0;
console.log(pokemon.skill.b.power) // 90
console.log(pokemon.skill == obj2.skill) // false
浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
我们平时实现方式很多如:
// 1
let array = [...array]
let obj1 = {...obj}
// 2
Object.assign(obj)
// 3
Array.prototype.concat()
Array.prototype.slice()
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
我们平时如果遇到非正则等特殊类型的数据,仅仅是普通对象或者数组的话,可以用 JSON.parse(JSON.stringify())来完成,也就是说,把对象转成字符串后,再把字符串转成对象,那么本身又开辟了一个新地址了。当然也可以通过递归手写实现。
实现
- 浅拷贝
Object.assign = Object.assign || function () {
if (arguments.length == 0) throw new TypeError('Cannot convert undefined or null to object');
var target = arguments[0],
args = Array.prototype.slice.call(arguments, 1),
key
args.forEach(function (item) {
for (key in item) {
item.hasOwnProperty(key) && (target[key] = item[key])
}
})
return target
}
因为ES6就有了Object.assign,如果手写的话最好判断是否存在Object.assign,至于浅拷贝这个问题就是把第一层的内存地址,生成新对象返回回来。
- 深拷贝
function deepClone(obj1, obj2) {
var obj2 = obj2 || {};
var toStr = Object.prototype.toString;
var arrStr = toStr.call([]);
for (var prop in obj1) {
if (obj1.hasOwnProperty(prop)) {
if (obj1[prop] !== null && typeof (obj1[prop]) == "object") {
obj2[prop] = toStr.call(obj1[prop]) == arrStr ? [] : {};
deepClone(obj1[prop], obj2[prop]);
}
else {
obj2[prop] = obj1[prop];
}
}
}
return obj2;
}
这里我们用递归的写法反复判断其类型然后进行不同的拷贝任务,值得注意的是,普通对象和数组要区分出来。