谈谈你对浅拷贝和深拷贝的理解?

浅拷贝就是通过赋值的方式进行拷贝,只拷贝对象的引用,是对指针的拷贝,拷贝后两个指针指向同一个内存,同一份数据,意味着当原对象发生变化的时候,拷贝对象也跟着变化。

什么时候使用深拷贝?:后台返回数据,我们需要对数据进行操作,可能其他地方也需要使用这份数据,直接修改会导致很多其他隐性问题,使用深拷贝,就能让我们更安全,更安心的去操作这些数据了,因为反正我们复制了一份下来。

深拷贝:不但对指针进行拷贝,而且还对指针指向的内容进行拷贝,也就是另外申请了一块空间内存,内容和原对象一致,但是是两份独立的数据,更改原对象,拷贝对象不会发生变化。

大白话:假设B复制了A,当修改A时,如果B也跟着变了,说明只拷贝了指针,A,B实际共用一份数据,这是浅拷贝;如果A变,B没变,那就是深拷贝,复制对象不受原对象影响。因为不仅拷贝了指针,还拷贝了内容,他们自己有自己的内存,互相独立。

深拷贝的实现方式

(1) js内置对象的JSON对象的序列化和反对象化方法:JSON.parse + JSON.stringify

// 但是这个方法是有局限的,无法实现对对象中方法的深拷贝,取不到值为undefined的key等等之类的。function cloneDeepJson(obj){ return JSON.parse(JSON.stringify(obj))}

(2) 使用递归实现深拷贝。a. 传入的原对象,遍历其属性,每个属性需要判断它的值是否是object类,如果不是,说明是基本数据类型,可以直接赋值;b. 如果是object类,那么需要再具体判断这个数据是对象还是数组,是数组创建一个空数组[],是对象则创建一个空对象{},继续递归。

//递归实现深拷贝function deepClone(origin, target){ var target = target || {}; //防止不传target for(let key in origin){ //遍历origin中的属性 if(origin.hasOwnProperty(key)){ //判断自身是否有该属性而非原型链上的 if( origin[key] && typeof(origin[key]) == “object”){ //如果当前value是一个object类型,需要往下递归 target[key] = Array.isArray(origin[key]) ? [] : {}; //判断这个object类型,具体是数组还是对象 deepClone(origin[key], target[key]); //递归 }else{ target[key] = origin[key]; //如果当前value不是一个object类型,直接赋值即可 } } } return target; //返回最终的拷贝对象}

(3) loadash是一个很热门的函数库,我们引入这个库后,就可以直接使用这个方法了,但是如果项目本身没有引入这个库,就不要为了使用深拷贝专门引入整个库,这样有点得不偿失。(2) Object.assign(target,…sources) ES6新增的方法可以实现深拷贝,target: 拷贝给谁。 sources: 拷贝的对象。

// 注意:只有当对象中没有嵌套对象时,才可以实现深拷贝const foo = { name: ‘张三’, age: 24}const newFoo = Object.assign({}, foo)foo.age = 25console.log(foo, newFoo) // {name: ‘张三’, age: 25} {name: ‘张三’, age: 24}// 对象中有内嵌的对象时const foo = { name: ‘张三’, info: { age: 24 }}const newFoo = Object.assign({}, foo)foo.info.age = 25console.log(foo, newFoo) // { name: ‘张三’, info: { age: 25 } } { name: ‘张三’, info: { age: 25 } }

(3) for ··· in ,使用for ··· in 遍历赋值太麻烦,不推荐。只要碰到某一个复杂数据类型(数组、对象),就再次进入这个复杂数据类型进行二次遍历,如果还有就再进入继续遍历。(4) 通过 json 反序列化实现深拷贝。不管多复杂的数据类型,转换为json字符串以后就是基本数据类型,字符串的赋值就是基本数据类型的赋值,赋值以后再转换回来。(6) …展开运算符。拷贝一层是深拷贝,但是修改对象里数组的某一项就会受影响,说明是浅拷贝。

let obj11 = { name: “张三”, age: 18, hobbies: [‘sleep’, ‘eat’, ‘play’] } let obj12 = { …obj11 } obj12.name = “胡歌” obj12.hobbies[0] = ‘sleep + doDream’ console.log(‘——————–‘, obj11, obj12);

(7) arr.concat() 方法。只深拷贝最外层,修改数组里的对象就会受影响,是浅拷贝。

// 该方法用于数组合并,合并的数组.concat(被合并的数组元素…) // 参数可有多个,用逗号分隔,返回合并后的数组。 // 原理:用原数组去合并一个空数组,返回合并后的新数组。 let arr11 = [1, 3, { username: ‘coco’ }]; let arr22 = arr11.concat(); arr22[2].username = ‘xxxxx’; arr22[0] = 555; // console.log(arr, ‘arr’);1 console.log(‘arr11—————‘, arr11); console.log(‘arr22————–‘, arr22);

(8) arr.slice() 方法。只深拷贝最外层。

const arr1 = [1, 3, { username: ‘aaaaaa’ }] const arr2 = arr1.slice() //返回截取后的数组,这里没有截取掉任何项,相当于返回原数组。 // 修改堆内存中的值 arr2[0] = 5 arr2[2].username = ‘bbbbbb’ console.log(‘arr1—‘, arr1) console.log(‘arr2—‘, arr2)

发表评论