浅拷贝和深拷贝的概念主要是针对引用类型的。基本数据类型就是简单地赋值而已,而引用类型的复制则有两种情况:
- 共用同一个内存地址;
- 新开辟一个新的内存地址。
什么是赋值?
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
什么是浅拷贝?
重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
什么是深拷贝?
从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
赋值和浅拷贝的区别?
赋值是两个联动的对象,其中一个对象的属性值改变,不管这个属性值是基础数据类型还是引用类型,另一个对象都会跟着改变。而浅拷贝,只有当一个对象的引用类型的属性值改变时,才会影响另一个对象。举个例子:
const person = {
a: 1,
b: [1,2,3]
}
// 赋值(基础数据类型和引用数据类型都会跟着变)
const person1 = person
person1.a = 2
person1.b.push(4)
console.log(person) // { a: 2, b: [ 1, 2, 3, 4 ] }
console.log(person1) // { a: 2, b: [ 1, 2, 3, 4 ] }
// 浅拷贝(只有引用数据类型会跟着变)
const person2 = Object.assign({},person)
person2.a = 3
person2.b.push(5)
console.log(person) // { a: 2, b: [ 1, 2, 3, 4, 5 ] }
console.log(person2) // { a: 3, b: [ 1, 2, 3, 4, 5 ] }
// 深拷贝(基础数据类型和引用数据类型都不会变)
const person3 = JSON.parse(JSON.stringify(person))
person3.a = 3
person3.b.push(6)
console.log(person) // { a: 2, b: [ 1, 2, 3, 4, 5 ] }
console.log(person3) // { a: 3, b: [ 1, 2, 3, 4, 5, 6 ]
如何手写实现一个浅拷贝?
function shallowClone(obj) {
if (!obj) return obj
let res = {}
for (let key in obj) {
res[key] = obj[key]
}
return res
}
// test
const person = {
a: 1,
b: [1,2,3]
}
const person2 = shallowClone(person)
person2.a = 2
person2.b.push(4)
console.log(person) // { a: 1, b: [ 1, 2, 3, 4] }
console.log(person2) // { a: 2, b: [ 1, 2, 3, 4] }
其他浅拷贝的实现方式?
- Object.assign()
- 展开运算符...
- Array.prototype.concat()
- Array.prototype. slice()
const person = {
a: 1,
b: [1,2,3]
}
// 展开运算符...
const person2 = {...person}
person2.a = 2
person2.b.push(4)
console.log(person) // { a: 1, b: [ 1, 2, 3, 4 ] }
console.log(person2) // { a: 2, b: [ 1, 2, 3, 4 ] }
// Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
// Array.prototype. slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]
如何手写实现一个深拷贝?
function deepClone(obj, hash = new WeakMap()) {
if (!obj) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj
// 是对象的话就要进行深拷贝
// 使用哈希表,是为了减少遍历次数
if (hash.get(obj)) return hash.get(obj)
let cloneObj = Array.isArray(obj) ? [] : {}
// cloneObj 有可能是对象或者数组
// 也可以这么写
// let cloneObj = new obj.constructor()
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
// test
const person = {
a: 1,
b: [1,2,3]
}
const person3 = deepClone(person)
person3.a = 2
person3.b.push(4)
console.log(person) // { a: 1, b: [ 1, 2, 3 ] }
console.log(person3) // { a: 2, b: [ 1, 2, 3, 4 ] }
其他深拷贝的实现方式?
- JSON.parse(JSON.stringify())
这个方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。