一 浅拷贝
浅拷贝: 当拷贝的数据是对象,对象里的
1) 属性值是基本数据类型,那么拷贝的是地址,
2) 属性值是复杂引用类型,那么拷贝的是地址
浅拷贝只解决了第一层的问题,拷贝的第一层如果是引用类型,拷贝的还是地址
1.1 对象的浅拷贝
1.1.1 Object.assgin()方法
Object.assgin()
//将所有可以枚举属性的值从一个或多个源对象复制到目标对象,返回目标对象
let obj1 = {
a : 1,
b : {
c : 2,
},
d: [3,4]
}
let obj = Object.assign({},obj1)
//修改obj里属性值为引用类型的值
obj.b.c = 3
obj.d[0] = 4
obj.d[1] = 5
console.log("obj",obj)
console.log("obj1",obj1)
我们发现除了基本数据类型不变之外,引用类型的值都改变了,所以object.assgin()是浅拷贝,只解决了第一层.
1.1.2 对象展开运算符方法
let obj1 = {
a : 1,
b : {
c : 2,
},
d: [3,4]
}
let obj = {...obj1}
//修改obj里属性值为引用类型的值
obj.a = 2
obj.b.c = 3
obj.d[0] = 4
obj.d[1] = 5
console.log("obj",obj)
console.log("obj1",obj1)
结果与上面是一样的:
1.2 数组的浅拷贝
数组的浅拷贝同样与对象一样,如果元素是基本数据类型拷贝的是值,是引用类型拷贝的是地址
1.2.1 slice()方法
let arrTest = [1,2,3,{a: 4},[5,6]]
//数组的浅拷贝
let arr = arrTest.slice(0)
//slice()方法 第一个参数是开始的位置 第二个参数是结束的位置
// 不会影响原数组,返回新的截取数组
arr[1] = 3
arr[3].a = 5
arr[4][0] = 6
console.log("arrTest",arrTest)
console.log("arr",arr)
数组的slice拷贝遇到的问题与对象一致
1.2.2 concat()方法
let arrTest = [1,2,3,{a: 4},[5,6]]
//数组的浅拷贝
let arr = [].concat(arrTest)
arr[1] = 3
arr[3].a = 5
arr[4][0] = 6
console.log("arrTest",arrTest)
console.log("arr",arr)
得到的结果与上面一样,这里很多人会问是不是可以用数组的展开运算符,来拷贝数组,这种方法也是可行的.
//数组的展开运算符
let arrTest = [1,2,3,{a: 4},[5,6]]
let arr = [...arrTest]
1.3 实现浅拷贝
//判断是数组还是对象
function isObjectOrisArray(data) {
if( Object.prototype.toString.call(data) == '[object Object]' ) {
return 'object'
} else if( Object.prototype.toString.call(data) == "[object Array]" ) {
return 'array'
}else {
return false
}
}
//浅拷贝实现
function cloneShallow(source) {
//判断数据既不是是数组又不是对象
if(!isObjectOrisArray(source)) return
//判断数据是数组还是对象
let target = isObjectOrisArray(source) == 'object'? {} : []
for(let key in source) {
//避免遍历从原型链到继承下来的可遍历的属性
if(Object.prototype.hasOwnProperty.call(source,key)) {
target[key] = source[key]
}
}
return target
}
测试数据:
Object.prototype.method=function(){
console.log(this);
}
let sourceObj = {
a: 1,
b: 2,
c: {
d: 4
}
}
sourceObj.__proto__.f = 1
let targetObj = cloneShallow(sourceObj)
targetObj.c.d = 5
console.log(sourceObj,targetObj)
二 深拷贝
深拷贝会拷贝所有的属性,并指向的动态分配的内存,深拷贝相比于浅拷贝速度较慢并且花销较大,拷贝前后两个对象互不影响.
JSON.parse(JSON.stringfy(Object))
let obj1 = {
a : 1,
b : {
c : 2,
},
d: [3,4]
}
let obj = JSON.parse(JSON.stringify(obj1))
//修改obj里属性值为引用类型的值
obj.b.c = 3
obj.d[0] = 4
obj.d[1] = 5
console.log("obj",obj)
console.log("obj1",obj1)
这两个对象的就是相互对立
但是该方法有以下几个问题:
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用问题
- 不能正确处理new Date()
- 不能处理正则
undefined symbol 函数这三种情况,会直接忽略
2.2 实现深拷贝
function isObjectOrisArray(data) {
if( Object.prototype.toString.call(data) == '[object Object]' ) {
return 'object'
} else if( Object.prototype.toString.call(data) == "[object Array]" ) {
return 'array'
}else {
return false
}
}
function cloneDeep(source,hash = new WeakMap()) {
//判断数据既不是是数组又不是对象
if(!isObjectOrisArray(source)) return
if(hash.has(source)) return hash.get(source)
console.log(hash)
//判断数据是数组还是对象
let target = isObjectOrisArray(source) == 'object'? {} : []
hash.set(source,target)
for(let key in source) {
//避免遍历从原型链到继承下来的可遍历的属性
if(Object.prototype.hasOwnProperty.call(source,key)) {
if(isObjectOrisArray(source[key])) {
//递归写法
target[key] = cloneDeep(source[key],hash)
}else {
target[key] = source[key]
}
}
}
return target
}