浅谈js深浅拷贝,并实现
js的基本数据类型有:Boolean、Null、Undefined、Number、String、Symbol
对象类型有:Object、Array、Function 以及特殊的RegExp和Date
其中基本数据类型是直接存贮在栈中,而对象类型呢是将名存储在栈中,真实的数据值存放在堆内存里,在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
一、浅拷贝
在对一个对象进行浅拷贝时,会创建一个新的对象,有一个新的地址,该对象中的基本数据类型的属性也会在新的地址中进行存放,与旧值将没有关系,而该对象中的引用数据类型则无法这样,因为新对象对复杂数据类型拷贝的仅是在栈中存放的属性名以及所指向堆中数据的指针地址,这样就会导致新旧对象中的复杂数据类型会共享同一个堆内存的空间,会相互影响。
简单实现浅拷贝:
var person = {
name: '张三',
hobby: ['跑步']
}
function shallowClone(source) {
let target = {}
for (let i in source) {
//hasOwnProperty() 方法返回一个布尔值,表示对象自有属性(而不是继承来的属性)中是否具有指定的属性
if (source.hasOwnProperty(i)) {
target[i] = source[i]
}
}
return target
}
let shallowPerson = shallowClone(person)
shallowPerson.name = '王五'
shallowPerson.hobby[0] = '打篮球'
console.log("🚀 ~ shallowPerson.name:", shallowPerson.name)//王五
console.log("🚀 ~ shallowPerson.hobby:", shallowPerson.hobby)//['打篮球']
console.log("🚀 ~ person.name:", person.name)//张三
console.log("🚀 ~ person.hobby:", person.hobby)//['打篮球']
可见上方中person中的hobby因为shallowPerson对象中hobby的改变也被跟着改变了
二、深拷贝
深拷贝就修复了浅拷贝对内部的复杂数据类型无法完全拷贝的问题,对复杂数据类型也会在堆中开辟新的地址,新旧对象的改变也不会影响彼此。
2.1 JSON.parse(JSON.stringify())
使用JSON.parse(JSON.stringify())可以实现对对象的深拷贝,但是存在不能拷贝function、Date、RegExp的弊端
var person = {
name: '张三',
hobby: ['跑步']
}
let deepPerson1 = JSON.parse(JSON.stringify(person))
deepPerson1.name = 'deep1Name'
deepPerson1.hobby[0] = 'deep1hobby'
console.log("🚀 ~ deepPerson1.name:", deepPerson1.name)//deep1Name
console.log("🚀 ~ deepPerson1.hobby:", deepPerson1.hobby)//['deep1hobby']
console.log("🚀 ~ person.name:", person.name)//张三
console.log("🚀 ~ person.hobby:", person.hobby)//['跑步']
2.2 简单实现
var person = {
name: '张三',
hobby: ['跑步']
}
function deepClone(source) {
//instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
if (source === null || typeof source !== 'object' || source instanceof Date || source instanceof RegExp) {
return source
}
// 递归的出口
if (typeof source !== 'object') return source
let obj = Array.isArray(source) ? [] : {}
for (var i in source) {
//hasOwnProperty() 方法返回一个布尔值,表示对象自有属性(而不是继承来的属性)中是否具有指定的属性
if (source.hasOwnProperty(i)) {
obj[i] = deepClone(source[i]) //递归
}
}
return obj
}
let deepPerson2 = deepClone(person)
deepPerson2.name = 'deep2Name'
deepPerson2.hobby[0] = 'deep2hobby'
console.log("🚀 ~ deepPerson2.name:", deepPerson2.name)//deep2Name
console.log("🚀 ~ deepPerson2.hobby:", deepPerson2.hobby)//['deep2hobby']
console.log("🚀 ~ person.name:", person.name)//张三
console.log("🚀 ~ person.hobby:", person.hobby)//['跑步']