说到深拷贝与浅拷贝数据类型是无法忽略的门槛
数据类型分为基本数据类型和引用(对象)数据类型
基本数据类型:(String, Number, boolean, Null, Undefined,Symbol)
引用(对象)数据类型: Object,特殊的有Array, Function, Date, Math, RegExp, Error等
JS的内存跟其他的内存差不多,分为堆(heap)和栈(stack)。
堆:由程序员手动分配内存,并且手动销毁(高级语言如JS中有垃圾自动回收机制),效率不如栈,但容量大。
浅拷贝
var obj1 = {
name: "你我贷"
}
var obj2 = {}
obj2 = obj1;
console.log(obj1 === obj2) // true
console.log(obj2.name) // "你我贷"
obj2.name = "飞旋"
console.log(obj1 === obj2) // true
console.log(obj1.name) //飞旋
问题?
var obj1 = {
name: ["嘉银","你我贷"]
}
var obj2 = {}
obj2 = obj1;
console.log(obj1 === obj2) //true
console.log(obj2.name) //["嘉银", "你我贷"]
obj2.name.push("飞旋");
console.log(obj1.name) //["嘉银", "你我贷", "飞旋"]
console.log(obj1 === obj2) //true
注意:浅拷贝有个很大的问题就是 如果父对象的属性是一个 数组或者是另一个对象的话 那么子对象获得的只是一个内存地址,而不是真正的拷贝,因此父对象容易存在被篡改的可能
(JS的基本类型分配在栈中,而因为引用类型大小的不固定,系统将存储该引用类型的地址存在栈中,并赋值给变量本身,而具体的内容存在堆中。所以当访问一个对象的时候,先访问栈中它的地址,然后按照这个地址去堆中找到它的实际内容。
所以当复制的时候,对于基本类型的变量,系统会为新的变量在栈中开辟一个新的空间,赋予相同的值,然后这两个变量就各自独立,毫无牵连。而对于引用类型的变量,新的变量复制的是那个对象在堆中的地址,这两个变量指向的是同一个对象。obj2复制了obj之后,两个其实指向的是同一个对象。)
1)封装方法实现浅拷贝
var obj1 = {
name: "嘉银",
address: "上海"
}
function shallowCopy (o) {
var obj = obj || {}
for(var prop in o) {
obj[prop] = o[prop]
}
return obj
}
var obj2 = shallowCopy(obj1);
console.log(obj1,obj2)
2)通过ES6 Object.assign()方法实现浅拷贝
var obj1 = {
name: "嘉银",
address: "上海"
}
var obj2 = Object.assign(obj1)
obj2.name = "飞旋"
console.log(obj1,obj2)
3)通过 Array.prototype.concat()方法实现浅拷贝
var arr1 = ["你我贷","飞旋",{
name: "百味"
}]
var arr2 = arr1.concat()
arr2[2].name = "金融"
console.log(arr1,arr2)
4)通过Array.prototype.slice()方法实现浅拷贝
var arr1 = ["你我贷","飞旋",{
name: "百味"
}]
var arr2 = arr1.slice()
arr2[2].name = "金融"
console.log(arr1,arr2)
特别注意:Array.prototype.slice() 和 Array.prototype.concat() 如果修改新对象里面的属性的数据类型不是对象的话 是不会影响原数组的
var arr1 = ["你我贷","飞旋",{
name: "百味"
}]
var arr2 = arr1.slice()
arr2[1] = "金融"
console.log(arr1,arr2)
5)ES6 展开运算符实现浅拷贝
var obj1 = {
name: "你我贷"
}
var obj2 = { ...obj1}
console.log(obj2)
深拷贝
1)通过递归方法实现深拷贝
var obj1 = {
name: ["嘉银","你我贷"],
address: "上海",
job: {
type: "开发",
section: "投资"
}
}
function checkType(tar) {
return Object.prototype.toString.call(tar)
}
function deepCopy(target,obj) {
if(checkType(target) === "[object Object]") {
obj = {};
}else if(checkType(target) === "[object Array]") {
obj = [];
}else {
return target
}
for(var prop in target) {
if(checkType(target[prop]) === "[object Object]"
|| checkType(target[prop]) === "[object Array]") {
obj[prop] = deepCopy(target[prop])
}else {
obj[prop] = target[prop]
}
}
return obj
}
var obj2 = deepCopy(obj1);
console.log(obj2)

2)JSON.parse(JSON.stringify())方法实现深拷贝
var obj1 = {
name: ["嘉银","你我贷"],
address: "上海",
job: {
type: "开发",
section: "投资"
}
}
var obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2)
原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
注意:json.stringfy()将对象、数组转换成字符串;json.parse()将字符串转成json对象
弊端:这种方法虽然可以实现数组或对象深拷贝,但不能处理函数
var obj1 = {
name: ["嘉银","你我贷"],
address: "上海",
job: {
type: "开发",
section: "投资"
},
say: function() {
}
}
var obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj1,obj2)
总结:浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
扩展知识点
Object.assign():
解释:方法将所有可枚举属性的值从一个或多个源对象复制到目标对象,然后返回目标对象
语法:Object.assign(target,..sources);
参数 :target:目标对象 sources:源对象
返回值:target:目标对象
var arr1 = ["你我贷","飞旋"]
var arr2 = []
var arr3 = Object.assign(arr1,arr2)
console.log(arr3)