- 引入的赋值:指向同一个对象,相互之间会影响;
- 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响;
- 对象的深拷贝:两个对象不再有任何关系,不会相互影响;
如果有一个对象info,想要拷贝里面的内容,有三种方式。
const info ={
name: "zzz",
age: 18,
friend: {
name: "www"
}
}
方式一:使用引用赋值
info和obj1是相同的引用,修改obj1的name也会影响到info的name。
//引用赋值
const obj1 = info
console.log(obj1)
方式二:浅拷贝
//浅拷贝
const obj2 = {...info}
obj2.name = "jx"
console.log(obj2.name) //jx
使用展开运算符,拷贝了一个新的对象obj2,修改里面的name,info不会跟着改变。
const obj2 = {...info}
obj2.friend.name = "jx"
console.log(obj2.friend.name) //jx
console.log(info.friend.name) //jx
但是如果修改的是对象,info的对象也会跟着发生变化。
浅拷贝可以拷贝原始类型,不能拷贝复杂类型比如对象等。
还有一种浅拷贝的方式。
const obj3 = Object.assign({}, info)
Object.assign方法,将后面的info拷贝到前面的{}对象中,并且返回给obj3。
方式三:深拷贝
深拷贝实现方式有两种:使用JSON方法,自己实现
使用JSON方法
const obj4 = JSON.parse(JSON.stringify(info))
这个obj4完全是一个新的对象,修改对象的内容,info的对象内容也不会跟着改变。
但是使用JSON方法有很大的缺陷就是当info里面有其他的值时。
const info ={
name: "zzz",
age: 18,
friend: {
name: "www"
},
running: function() {},
[Symbol()]: "abc"
}
深拷贝下来的内容是没有函数和Symbol的。
我们可以自己手写深拷贝函数。
深拷贝函数的基本类型和对象的实现
首先需要创建一个函数用来判断一个value是否为对象。
注意不能用typeof 来判断value是否为null,因为null用typeof判断,出来的是object。
function isObject(value){
const valueType = typeof value
return (value !== null) && (valueType === "function" || valueType === "function")
}
function deepCopy(originValue){
//如果是原始类型就直接返回
if(!isObject(originValue)){
return originValue
}
//创建一个新的对象
const newObj = {}
//遍历要拷贝的对象,取出它的key
//递归 重新判断要拷贝的对象里面是否还有对象
for(const key in originValue){
newObj[key] = deepCopy(originValue[key])
}
//返回新对象
return newObj
}
关键的代码是,使用递归不断判断要拷贝的对象里面是否还有对象。
用一个对象来实验一下深拷贝的效果吧。这个对象暂时还没有函数和Symbol类型的判断,后面会做优化的。
const info ={
name: "zzz",
age: 18,
friend: {
name: "www",
address: {
name: "sz",
detail: "lky"
}
},
}
const newObj = deepCopy(info)
console.log(newObj)
深拷贝成功了!
深拷贝函数的数组拷贝
有时候我们需要拷贝数组,在JavaScript中,数组也是一个对象,上面的代码中创建的是一个新的对象,但不一定要创建的是新对象,也有可能是新数组,所以需要先区分对象和数组,再创建对象或者数组。
function isObject(value) {
const valueType = typeof value
return (value !== null) &&(valueType === "object" || valueType ==="function")
}
function deepCopy(originValue) {
//如果是原始类型就直接返回
if(!isObject(originValue)) {
return originValue
}
//判断是数组还是对象
const newObj = Array.isArray(originValue)? [] :{}
for(const key in originValue){
newObj[key] = deepCopy(originValue[key])
}
return newObj
}
关键的一步是通过Array的isArray判断传入的是对象还是数组。
我们创建一个books数组来验证一下是否可以判断对象还是数组来拷贝。
const books = [
{ name:"book1", price: 1 },
{ name:"book2", price: 2 },
{ name:"book3", price: 3 },
]
const newArray = deepCopy(books)
console.log(newArray)
拷贝的是一个数组。
深拷贝函数的Set拷贝
有的时候,深拷贝的内容有Set,如果不知道Set是什么的话,可以看这篇JavaScript中Set的使用,Set和数组类似,但是Set的元素是不能重复的,所以它常用于去重。
创建一个set,把它放进info对象里面。
const set = new Set(["zzz", "wjx", "lky"])
const info = {
name: "zzz",
age: 18,
friend: {
name: "www",
address: {
name: "sz",
detail: "lky"
}
},
set: set
}
function isObject(value) {
const valueType = typeof value
return (value !== null) &&(valueType === "object" || valueType ==="function")
}
function deepCopy(originValue) {
//如果是原始类型就直接返回
if(!isObject(originValue)) {
return originValue
}
//判断是否是set
if(originValue instanceof Set) {
const newSet = new Set()
for(const setItem of originValue) {
newSet.add(deepCopy(setItem))
}
return newSet
}
//判断是数组还是对象
const newObj = Array.isArray(originValue)? [] :{}
for(const key in originValue){
newObj[key] = deepCopy(originValue[key])
}
return newObj
}
先判断传入的值是不是Set,创建新的Set,遍历Set,把遍历的元素一个个放到新的Set里面。
可以拷贝Set了。
深拷贝函数的函数返回
函数是不需要深拷贝的,因为函数是用来执行的,如果再拷贝一个函数,会消耗性能。
if(typeof originValue === "function") {
return originValue
}
所以遇到函数的时候直接返回了。