携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情 >>
深浅拷贝是什么
学习深浅拷贝前得先了解 js 的两种数据类型:
-
基本类型:String、Number 等,在使用时,会被放到栈中。当有新值时就会开辟新的内存空间。
-
引用类型:Array、Object 等,在使用时,会被放到堆中。当有新的对象和数组时 js 就会在堆中开辟新的内存地址,这个地址存在指针(引用)与对象(值)。
简述:
在复制基本类型的值时,js 会直接将栈中的数据取出复制给新值,并开辟出一个新的内存空间用来存储新的被复制的值。
而复制引用类型时,复制对象仅仅只会复制被复制对象的指针,也就是说新复制的对象并没有得到一个新的内存地址,其指针,也就是引用还是指向被复制的对象。
一、浅拷贝(Shallow copy)
复制对象与被复制对象使用的还是同一个内存地址上的值。
二、深拷贝(Deep copy)
复制对象与被复制对象使用的不是同一个内存地址中的对象,在复制对象复制被复制对象时 js 会在堆中新开辟一个内存地址用来存储复制对象的指针。
三、主要区别:
A 复制了 B 对象,在修改 B 对象时,如果 A 对象的值也被修改了,这就是浅拷贝,反之 A 的值没有被修改,那就是深拷贝。
1. 浅拷贝实例:
// 对象1
let shallowCopy1 = {
id: 1,
age: 18,
sex: '男',
name: '张三'
}
// 对象2 复制 对象1
let shallowCopy2 = shallowCopy1
// 修改 对象2
shallowCopy2.age = 19
// 对象 1, 2 输出内容一致
console.log(shallowCopy1)
console.log(shallowCopy2)
输出内容一致:
2. 深拷贝实例:
// 对象1
let deepCopy1 = {
name: '李四',
age: '男'
}
// 使用 JSON.parse(JSON.stringify(Object)) 深拷贝对象 1
let deepCopy2 = JSON.parse(JSON.stringify(deepCopy1))
// 修改 对象2
deepCopy2.name = '王五'
// 对象 1, 2 输出内容不一致
console.log(deepCopy1)
console.log(deepCopy2)
3. 浅拷贝实现方式总结:
1. Object.assign()
// 对象 1
let shallowCopy1 = {
person: {
name: "kobe",
age: 41
},
sports:'basketball'
}
// 使用 Obje.assign 方法浅拷贝对象 1
let shallowCopy2 = Object.assign({}, shallowCopy1)
// 修改对象 2
shallowCopy2.person.name = "wade"
2. 展开运算符
就代码量来说,需要浅拷贝的时候,使用展开运算符是最好的。
// 对象 1
let shallowCopy1 = {
name: 'Kobe',
address:{
x:100,
y:100
}
}
// 使用展开运算符浅拷贝对象1
let shallowCopy2= {... shallowCopy1}
3. 数组的 .concat() 合并方法
// 数组 1
let shallowCopy1 = [1, 3, {
username: 'kobe'
}]
// 使用数组合并方法浅拷贝数组 1
let shallowCopy2 = shallowCopy1.concat()
4. 数组的 .slice() 方法
// 数组1
let shallowCopy1 = [1, 3, {
username: ' kobe'
}]
// 使用数组 array.slice() 方法浅拷贝数组 1
let shallowCopy2 = shallowCopy1.slice()
5. 使用 Array.from 实现
// 数组1
let shallowCopy1 = [1, 3, {
username: ' kobe'
}]
// 使用数组 array.slice() 方法浅拷贝数组 1
let shallowCopy2 = Array.from(shallowCopy1)
4. 深拷贝实现方式总结:
1. 使用 JSON.parse(JSON.stringify(Object))
实现:先将数据转为 JSON 字符串,后又转回 js 对象。缺点是不能转为函数和正则。
// 对象 1
let deepCopy1 = {
person: {
name: "kobe",
age: 41
},
sports:'basketball'
}
// 使用 json 转换对象深拷贝对象 1
let deepCopy2 = JSON.parse(JSON.stringify(deepCopy1))
// 修改对象 2
deepCopy2.person.name = "wade"
2. 使用递归
实现:使用递归将数组和对象中的每一项都遍历出来,再去把值复制出来,也是深拷贝的一种实现方式。缺点也很明显,代码量太大了。而且需要用到递归。
function deepClone(obj){
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === 'object') {
for(let key in obj){
//判断对象的这条属性是否为对象
if (obj[key] && typeof obj[key] === 'object'){
//若是对象进行嵌套调用
objClone[key] = deepClone(obj[key]);
}else{
objClone[key] = obj[key]
}
}
}
//返回深度克隆后的对象
return objClone;
}
3. 使用 jQuery 中的 .extend() 方法
// 对象1
var deepCopy1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
}
// 使用 jQuery.extend() 复制对象1
var deepCopy2 = $.extend(true, {}, deepCopy1)
// 输出:false
console.log(deepCopy1.b.f === deepCopy2.b.f)
4. 使用 for...in
function deepClone(obj) {
var result = {}
if (obj && typeof obj === 'object') {
for (let key in obj) {
if (obj[key] && typeof obj[key] === 'object') {
// 如果对象的属性值为 object ,就递归调用 deepClone,即把某个值对象复制一份到新的对象中
result[key] = deepClone(obj[key]);
} else {
// 如果对象的属性值不为 object,就直接复制键值对到新的对象当中
result[key] = obj[key];
}
}
return result;
}
return obj;
}