1.浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
2.深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
浅拷贝只能拷贝一层对象,如果对象存在嵌套关系,他是不能拷贝的。
浅拷贝:
1.Object.assign()
先来看一个使用Object.assign正常的浅拷贝
var obj1 = {
a:1,
b:2,
c:{
d:3
}
}
var obj2 = {}
Object.assign(obj2,obj1)
obj2.a=11
obj2.c.d=33
console.log(obj1); -----> {a:1,b:2,c:{d:33}}}
console.log(obj2); -----> {a:11,b:2,c:{d:33}}
==注意==:
- 它不能拷贝对象的继承属性
- 不能拷贝对象的不可枚举属性
- 可以拷贝Symbol类型的属性
var obj1 = {
a:{
b:1
}
}
Object.defineProperty(obj1,'c',{
value: 3,
enumerable: false
})
var obj2 = {}
Object.assign(obj2,obj1)
console.log(obj1) ----> {a:{b:1},c:3}
console.log(obj2) ----> {a:{b:1}}
2.扩展运算符
var obj1 = {a:{b:1}}
var obj2 = {...obj1}
let arr = [1, 2, 3];
let newArr = [...arr];
3. Array.slice()方法
var obj1 = [1,2,{d:3}]
var obj2 = obj1.slice()
obj2[2].d = 4
console.log(obj1); ----> [ 1, 2, { d: 4 } ]
console.log(obj2); ----> [ 1, 2, { d: 4 } ]
4.Array.concat()方法
var obj1 = [1,2,{d:3}]
var obj2 = obj1.concat([])
obj2[2].d = 4
console.log(obj1); ----> [ 1, 2, { d: 4 } ]
console.log(obj2); ----> [ 1, 2, { d: 4 } ]
5.手写一个浅拷贝
function shallowClone(target){
let newResult = Array.isArray(target) ? [] : {}
if(typeof target === 'object'){
for(item in target){
newResult[item] = target[item]
}
return newResult
}else{
return target
}
}
var obj1 = [1,2,{d:3}]
var obj2 = shallowClone(obj1)
obj2[2].d = 4
console.log(obj1); ----> [ 1, 2, { d: 4 } ]
console.log(obj2); ----> [ 1, 2, { d: 4 } ]
深拷贝
1.JSON.parse(JSON.stringty()) 实现
function deepCloneByJson(target){
return JSON.parse(JSON.stringify(target))
}
var obj1 = [1,2,{d:3}]
var obj2 =deepCloneByJson(obj1)
obj2[2].d = 4
console.log(obj1);
console.log(obj2);
==注意==
- 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
- 拷贝Date,会变成字符串
- 无法拷贝不可枚举属性
- 无法拷贝对象的原型链
- 拷贝RegExp,会变成空对象{}
- 对象中有NaN 、Infinity、JSON序列化的结果会变成null
- 无法拷贝对象的循环应用
2.手写一个深拷贝
function isComplexTypeData(obj){
return (typeof obj === 'object') && obj!==null
}
function deepClone(obj, hash = new WeakMap()){
if(obj.constructor === Date) return new Date(obj)
if(obj.constructor === RegExp) return new RegExp(obj)
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
let desc = Object.getOwnPropertyDescriptors(obj);
let cloneObj = Object.create(Object.getPrototypeOf(obj), desc);
hash.set(obj, cloneObj);//继承原型链
for(let item in Reflet.ownKeys(cloneObj)){
cloneObj[item] = isComplexTypeData(obj[item]) && typeof !== 'function' ? deepClone(obj,hash) : obj[item]
}
return cloneObj;
}
上面的是手写代码,下面的是测试代码
// 下面是验证代码
let obj = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: { name: '我是一个对象', id: 1 },
arr: [0, 1, 2],
func: function () { console.log('我是一个函数') },
date: new Date(0),
reg: new RegExp('/我是一个正则/ig'),
[Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性
let cloneObj = deepClone(obj)
//拷贝完了、实验一下
cloneObj.arr.push(4)
cloneObj.func()
console.log('obj', obj)
console.log('cloneObj', cloneObj)