先了解一下js的数据类型
js数据类型分两种:
第一种是基本数据类型:
String
(字符串)、Number
(数字) 、Boolean
(布尔值)、Null
(空)、Undefined
(未定义)
第二种是引用数据类型:
Object
(对象)、Array
(数组)、Function
(函数)
基本数据类型的变量是存放在栈内存,引用数据类型是放在堆内存中的,基本数据类型保存的是值,而引用数据类型一般保存的是对象的地址。
如果我们单纯的复制的话,可能就只是复制对象的地址,那么这就是浅拷贝,如果是克隆了对象,改变了引用对象的地址,那么就是深拷贝。
一、 浅拷贝
对于浅拷贝而言,就是只拷贝对象的引用,而不深层次的拷贝对象的值,多个对象指向堆内存中的同一对象,任何一个修改都会使得所有对象的值修改,因为它们公用一条数据
二、深拷贝
我们在实际的项目中,肯定不能让每个对象的值都指向同一个堆内存,这样的话不便于我们做操作,所以自然而然的诞生了深拷贝
深拷贝作用在引用类型上!例如:Object
,Array
深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱的问题,使得我们可以多次使用同样的数据,而不用担心数据之间会起冲突。
例:
//浅拷贝
let obj = {a:1,b:{c:1}}
let obj1 = {...obj1}
obj.b === obj1.b
//深拷贝
let obj3 = {a:1,b:{c:1}}
let obj4 = {..obj3,b:{...obj3.b}}
obj3.b === obj4.b
为什么要进行深拷贝
首先看下如下代码
let a = b = 2
a = 3
console.log(a)
console.log(b)
let c = d = [1,2,3]
let e = f = {a:1,b:2,c:3}
c[0] = 2
e.a = 2
console.log(d[0])
console.log(f.a)
会发现,同一个Array或者Object赋值给两个不同变量时,变量指向的是同一个内存地址,所以就会造成其中一个变量改变属性值,同时改变了另外一个变量的对应属性值。
而大多数实际项目中,我们想要的结果是两个变量(初始值相同)互不影响。所以就要使用到拷贝(分为深浅两种)
深浅拷贝的区别:
简而化之:
浅拷贝和深拷贝的区别是:看复制的子对象是否在堆内存中指向一个新的地址
那么怎么实现一个深拷贝呢?
实现思路:
检查类型,判断类型是否为引用类型,是就进行深拷贝,否则浅拷贝
使用递归
检查环,判断当前引用是否指向自身,避免进入死循环
需要忽略原型
简易版实现代码(只处理了Object和Array两种引用类型):
function deepClone(obj){
// 判断当前对象是对象还是数组
let result = Array.isArray(obj)?[]:{};
if(obj && typeof obj === "object"){
for(let key in obj){
// 判断是否为自身属性
if(obj.hasOwnProperty(key)){
if(obj[key] && typeof obj[key] === "object"){
//子元素为对象,进入递归
result[key] = deepClone(obj[key]);
}else{
result[key] = obj[key];
}
}
}
}
return result;
}
深拷贝的三种方式
深拷贝的方法
//待拷贝的对象
let a = {
name: '张三',
grade: {
chinese: 23,
math: 90,
},
sex: '男',
friend: [{id: '李四'},'王五'],
date: new Date().toString(),
call() {
console.log('call()')
}
}
JSON 对象
JSON.parse(JSON.stringify(a)): a 只能是扁平对象
//不能拷贝 function,会直接丢弃,ye
console.log('JSON 深拷贝a', JSON.parse(JSON.stringify(a)))
Object.assign(target,source)
//如果只有一级属性,则就是深拷贝
//如果一级属性里面有引用数据类型,则这个只是浅拷贝了
console.log(Object.assign({},a))
for in 递归拷贝
/* for in 递归拷贝会将 原型上的属性方法全部拷贝过来 */
function clone(source) {
let target = source instanceof Array ? [] : {}
for (let key in source) {
if (typeof source[key] === 'object') {
target[key] = clone(source[key])
} else {
target[key] = source[key]
}
}
return target
}
//函数还是同一个函数,函数没有做到深拷贝