深拷贝和浅拷贝的概念:
深拷贝拷贝的是对象或者数组内部数据的实体,重新开辟了内存空间存储数据;
浅拷贝拷贝的是引用类型的指针,副本和原数组或对象指向同一个内存;
另一个前提:实现浅复制和深复制的方法区分要看该方法是否对存在于数组或对象的深层的引用类型的值进行真实值的复制,而不是仅对引用进行拷贝,如果这样理解,那么下面的标题的定义应该要做一些修改。
一个方法是是实现了深拷贝还是浅拷贝,要看它是否在拷贝对象或数组时,出现拷贝指针的情况,出现了就是实现了浅拷贝,否则就是实现了深拷贝;
一、一些建立在数组中的各项或对象中的属性值均为基本类型值的前提下的深拷贝的例子
数组
1.concat方法
var arr1 = [1, 2, 3, 4]
var arr2 = arr1.concat()//复制当前数组并返回实现深拷贝的副本,arr1独立而不受影响
console.log(arr2);[1, 2, 3, 4]
var arr3 = arr1.concat([5, 6, 7])//将数组中的每一项都添加到深拷贝的副本数组中
console.log(arr3);//[1, 2, 3, 4, 5, 6, 7]
2.slice方法
var arr = [1, 2, 3, 4]
var arr1 = arr.slice(1)//接收1到2个参数,一个参数时,返回参数指定调用方法数组位置到末尾的值组成的深拷贝副本
console.log(arr1);//[2, 3, 4]
var arr2 = arr.slice(0, 1)
console.log(arr2);//[1]
3.扩展运算符
扩展运算符将一个数组转为用逗号分隔的参数序列
const arr = [1, 2, 3]
const arr1 = [...arr]
arr1.push(4)
console.log(arr1);//[1, 2, 3, 4]
console.log(arr);//[1, 2, 3]
注意,对于数组项是引用类型的数据,就无法实现深拷贝了。
const arr = [1, 2, 3, [4, 5, 6]]
const arr1 = [...arr]
console.log(arr1);
arr1[3].push(7)
console.log(arr1);
console.log(arr);//[1, 2, 3, [4, 5, 6, 7]]更改副本,原数组也被改变
对象
先看一下浅拷贝的例子
var obj = {
a: 1,
b: 2
}
var obj1 = {}
obj1 = obj//浅拷贝
console.log(obj1);//{a: 1, b: 2}
obj1.c = 3
console.log(obj);//{a: 1, b: 2, c: 3},更改obj1,obj也被改变
1.Object.assign(目标对象, 源对象),源对象的所有可枚举属性都复制到目标对象上
var obj = {
a: 1,
b: 2
}
var obj1 = {}
Object.assign(obj1, obj)
console.log(obj1);//{a: 1, b: 2}
obj1.c = 3
console.log(obj1);//{a: 1, b: 2, c: 3}
console.log(obj);//{a: 1, b: 2},obj没有被改变,实现了深拷贝
2.JSON
var obj = {
a: 1,
b: 2
}
// 将javascript值转为JSON字符串
var jsonText = JSON.stringify(obj)
console.log(jsonText);//{"a":1,"b":2}
// 把JSON字符串转为javascript值
var obj1 = JSON.parse(jsonText)
console.log(obj1);//{a: 1, b: 2}
// 修改obj1,看原对象值是否被改变
obj1.c = 3
console.log(obj1);//{a: 1, b: 2, c: 3}
console.log(obj);//{a: 1, b: 2},没被改变,实现了深拷贝
3.扩展运算符
var obj = {
a: 1,
b: 2
}
var obj1 = {...obj}
console.log(obj);//{a: 1, b: 2}
console.log(obj1);//{a: 1, b: 2}
obj.a = 33
console.log(obj);//{a: 33, b: 2}
console.log(obj1);//{a: 1, b: 2}没改变,实现了深拷贝
二、完全实现深拷贝的方法
1.JSON方法
var obj = {
a: {
c: 2,
d: [9, 8, 7]
},
b: 4
}
var jsontext = JSON.stringify(obj)
var obj1 =JSON.parse(jsontext)
console.log(obj);
console.log(obj1);
obj.a.d[0] = 666
console.log(obj);
console.log(obj1);
注意,此处代码放在node环境中运行才会得到期望的结果;
原因为:
并没有什么规范或一组需求指定console.* 方法族如何工作——它们并不是JavaScript 正式
的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。
尤其要提出的是,在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。
《你不知道的javascript(中卷)》
2.函数库lodash的_.cloneDeep方法
var _ = require('lodash')
var obj = {
a: {
c: 2,
d: [9, 8, 7]
},
b: 4
}
var obj1 = _.cloneDeep(obj)
console.log(obj === obj1);//false
3.递归实现深拷贝
function copy(object) {
// 判断传入的参数是数组还是对象
let target = object instanceof Array ? [] : {}
for (const [k ,v] of Object.entries(object)) {
target[k] = typeof v == 'object' ? copy(v) : v
}
return target
}
var obj1 = copy(obj)
console.log(obj.a.d === obj1.a.d);//false