本文是看过了别人的文章后的总结,如果想看看大佬写的,请访问:JavaScript基础心法——深浅拷贝
js中的数据类型分位基础数据类型和引用数据类型,基础数据类型的值存放在栈中,而引用数据类型的值存放在堆中,栈中存放的是堆的地址。所以对于基础数据类型而言,并没有深拷贝与浅拷贝的区别,对于引用数据类型而言,如果直接使用 ‘ = ’ 进行拷贝,那么会拷贝的是栈中存放的堆地址,从而造成堆中数据改变,所有指向该堆地址的数据都会发生改变。
浅拷贝
let array = [ 1 , 2 , 3 , 4 ]
let obj = {a:'1' , b:'2' , c:'3' }
let cloneArray = array
let cloneObj = obj
console.log(cloneArray) // [ 1 , 2 , 3 , 4 ]
console.log(cloneObj) // {a:'1' , b:'2' , c:'3' }
array.push(5)
obj.d = '4'
console.log(cloneArray) // [1, 2, 3, 4, 5]
console.log(cloneObj) //{a: "1", b: "2", c: "3", d: '4'}
可以看到,当我们改变array / obj中的数据,我们克隆的array和obj也发生了相应的改变。这便是因为,我们只进行了浅拷贝,只是复制了堆地址。
深拷贝
深拷贝不是像浅拷贝一般,只会拷贝堆地址的引用,而会复制其键值。其实现的方式有两种简单的方法。
1.使用JSON对象的parse与stringify方法。
2.使用递归函数
深拷贝的JSON方法
JSON对象中的stringify方法,可以使一个js值转化为一个JSON字符串,而parse方法,可以将一个JSON字符串转化为js值。
let array = [ 1 , 2 , 3 , 4 ]
let obj = {a:'1' , b:'2' , c:'3' }
let stringifyArray =JSON.stringify(array)
let stringifyObj =JSON.stringify(obj)
console.log(stringifyArray) // [1,2,3,4]
console.log(typeof stringifyArray) // string
console.log(stringifyObj) // {"a":"1","b":"2","c":"3"}
console.log(typeof stringifyObj) // string
let cloneArray = JSON.parse(stringifyArray)
let cloneObj = JSON.parse(stringifyObj)
console.log(cloneArray)//[1, 2, 3, 4]
console.log(typeof cloneArray) //object
console.log(cloneObj)//{a: "1", b: "2", c: "3"}
console.log(typeof cloneObj)//object
array.push(5)
obj.b = '4'
console.log(cloneArray)//[1, 2, 3, 4]
console.log(typeof cloneArray) //object
console.log(cloneObj)//{a: "1", b: "2", c: "3"}
console.log(typeof cloneObj)//object
可以看到,如果经过JSON对象的转换这一步,我们原始的array和obj,无论如何变动,克隆出的数据都不会随着原始数据进行变动。
但是值得注意的是,这个方法,并不完善。
let obj = {
a:'1',
b:function(){
return '2'
}
}
let cloneObj = JSON.parse(JSON.stringify(obj))
console.log(cloneObj) // {a:'1'}
可以看到,我们通过JSON对象转换而克隆出来的,其中键名为b,值为function的被忽略了。这是由于JSON对象的stringify方法造成的问题。
undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).
所以我们通过JSON对象进行转化的这种方法,并不适用于拷贝的对象中包含了undefined symbol function的情况,仅仅可以用在较为简单的数据中。
深拷贝的函数递归方法
function deepClone(source){
let targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){//因为for in 会访问到原型上的东西,所以这里使用hasOwnProperty
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
这样就可以完成一个深拷贝了。
JS深拷贝扩展
JS中 对象 数组 自带的可以实现拷贝的方法,如concat--数组拼接,slice--返回选中的段,Object.assign()--复制,...-- 展开运算符,等方法,都是实现的首层浅拷贝。也就是,拷贝的东西,仅在首层实现了深拷贝,而没有进行遍历,对更深层进行深拷贝。导致可以对深层的引用数据类型修改,从而改变拷贝后的东西。