1、相关概念
- 基本类型值(数值、布尔值、字符串、null和undefined):指的是保存在栈内存中的简单数据段。
- 引用类型值(对象、数组、函数、正则):指的是那些保存在堆内存中的对象,变量中保存的实际上只是一个指针,这个指针执行内存中的另一个位置,由该位置保存对象。
深拷贝和浅拷贝主要针对于引用类型数据,因为基本数据类型赋值后,改变新数据,不会影响到原来的数据;而引用数据类型赋值后,改变新数据,将会影响到原来的数据,此时应该使用深拷贝和浅拷贝定义出一个跟原数据一样但互不影响的数据。
-
浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
- 深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,前后的两个对象互不影响。
浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。
2、浅拷贝的实现方案:
(1)Object.assign(target, src)
let obj = { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } } let newObj = Object.assign({}, obj); obj.age = 25 obj.gf.age = 25; console.log(newObj, obj) //{ name: 'Lee', age: 18, gf: { name: 'Yjj', age: 25 } } { name: 'Lee', age: 25, gf: { name: 'Yjj', age: 25 } }
(2)扩展运算符(...)
let obj = { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } } let newObj = {...obj} obj.age = 25; obj.gf.age = 25 console.log(newObj, obj); //{ name: 'Lee', age: 18, gf: { name: 'Yjj', age: 25 } } { name: 'Lee', age: 25, gf: { name: 'Yjj', age: 25 } }
(3)Array.prototype.concat()
let arr = [1,2,4,5,[6,7]] let newArr = arr.concat() arr[0] = 100 arr[4][0] = 100 console.log(arr, newArr) //[ 100, 2, 4, 5, [ 100, 7 ] ] [ 1, 2, 4, 5, [ 100, 7 ] ]
(4)Array.prototype.slice()
let arr = [1,2,4,5,[6,7]] let newArr = arr.slice() arr[0] = 100 arr[4][0] = 100 console.log(arr, newArr) //[ 100, 2, 4, 5, [ 100, 7 ] ] [ 1, 2, 4, 5, [ 100, 7 ] ]
注意:如果对象中有数据的值是引用数据类型,在创建新对象的过程中,会将这个引用数据类型的地址也放到新对象中。
同理,如果数组中的数据有引用类型数据,上面两个方法对于数组的拷贝,会将这个引用类型数据的地址也拷贝出来。
(5)函数法
function shallowClone(obj) { if (!obj || typeof obj !== 'object') return let newObj = Array.isArray(obj) ? [] : {} for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key] } } return newObj } let obj = { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } } let newObj = shallowClone(obj) obj.age = 25 obj.gf.age = 25 console.log(obj, newObj) //{ name: 'Lee', age: 25, gf: { name: 'Yjj', age: 25 } } { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 25 } }
3、深拷贝的实现方法
(1)JSON
let obj = { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } } let newObj = JSON.parse(JSON.stringify(obj)) obj.age = 25 obj.gf.age = 25 console.log(obj, newObj) //{ name: 'Lee', age: 25, gf: { name: 'Yjj', age: 25 } } { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } }
(2)函数法(递归)
function deepClone(obj) { if (!obj || typeof obj !== 'object') return let newObj = Array.isArray(obj) ? [] : {} for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key] } } return newObj } let obj = { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } } let newObj = deepClone(obj) obj.age = 25 obj.gf.age = 25 console.log(obj, newObj) //{ name: 'Lee', age: 25, gf: { name: 'Yjj', age: 25 } } { name: 'Lee', age: 18, gf: { name: 'Yjj', age: 18 } }
4、深浅拷贝的应用
总之,赋值是赋值,拷贝是拷贝。浅拷贝,当对象或数组中的数据都是基本数据类型的时候,两个数据之间完全是独立的,如果对象或数组中的值是引用类型的时候,里面是引用类型的值,还是会保持共同的内存地址;深拷贝出来的两个数据是完全独立的。
浅拷贝:对于一层结构的Array和Object想要拷贝一个副本时使用、vue的mixin是浅拷贝的一种复杂型式
深拷贝:复制深层次的object数据结构