JavaScript深浅拷贝
ECMAScript中的数据类型可以分为两种:
基本类型:Undefined, Null, Boolean, String, Number, Symbol
引用类型:Object, Array, Function, RegExp等
不同类型的存储方式:
基本类型:基本类型值在内存中占据固定大小,保存在栈内存中
引用类型:引用类型的值是对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址
不同类型的复制方式:
基本类型:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量。
let foo = 1; let bar = foo; console.log(foo === bar); // => true // 修改foo的值并不会改变bar的值 foo = 10; console.log(foo); // => 10 console.log(bar); // => 1
引用类型:从一个变量向另外一个新变量复制引用类型的值,实际上复制的是指针,最终两个变量都指向同一个对象。
let foo = { name: 'deeper', age: 10 }; let bar = foo; console.log(foo === bar); // => true //修改foo的值会影响bar的值 foo.age = 20; console.log(foo); // => {name: 'deeper', age: 20} console.log(bar); // => {name: 'deeper', age: 20}
深拷贝 & 浅拷贝
浅拷贝:仅仅是复制了引用,彼此之间的操作会相互影响;
深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响;
深拷贝的实现:
一:利用递归实现对对象或数组的深拷贝
function deepCopy(obj) { if(!obj || typeof obj !== 'object') { throw new Error('error arguments'); } const targetObj = Array.isArray(obj) ? [] : {}; for(let key in obj) { if(obj.hasOwnProperty(key)) { if(obj[key] && typeof obj[key] === 'object') { targetObj[key] = deepCopy(obj[key]); } else { targetObj[key] = obj[key]; } } } return targetObj; }
原生JavaScript中提供的一些方法
1、Array.prototype.slice(start, end):从已有的数组中返回选定的元素。
let a = [1, 2, 3, 4]; let b = a.slice(); console.log(a === b); // -> false a[0] = 5; console.log(a); // -> [5, 2, 3, 4] console.log(b); // -> [1, 2, 3, 4]
but :
let a = [[1, 2], 3, 4]; let b = a.slice(); console.log(a === b); // -> false a[0][0] = 0; console.log(a); // -> [[0, 2], 3, 4] console.log(b); // -> [[0, 2], 3, 4]
2、Array.prototype.concat()
let a = [1, 2, 3, 4]; let b = a.concat(); console.log(a === b); // -> false a[0] = 5; console.log(a); // -> [5, 2, 3, 4] console.log(b); // -> [1, 2, 3, 4]
but:
let a = [[1, 2], 3, 4]; let b = a.concat(); console.log(a === b); // -> false a[0][0] = 0; console.log(a); // -> [[0, 2], 3, 4] console.log(b); // -> [[0, 2], 3, 4]
由此可见Array的slice和concat方法并不是真正的深拷贝,对于Array的第一维元素是深拷贝,而第二维的slice和concat方法是复制引用,所以,Array的slice和concat方法都是浅拷贝。
3、JSON.stringify()和JSON.parse()
- JSON.stringify():把一个js对象序列化为一个JSON字符串
- JSON.parse():把JSON字符串反序列化为一个js对象
let obj = { name: 'deeper', age: 20, friend: { name: 'lee', age: 19 } }; let copyObj = JSON.parse(JSON.stringify(obj)); obj.name = 'Sandman'; obj.friend.name = 'Jerry'; console.log(obj); // -> {name: "Sandman", age: 20, friend: {age: 19,name: 'Jerry'}} console.log(copyObj); // -> {name: "deeper", age: 20, friend: {age: 19,name: 'lee'}}
JSON.parse()和JSON.stringify()是完全的深拷贝。
深拷贝的实现也可以这样:
export const cloneObject = (source?: any) => { const json = JSON.stringify(source); const result = JSON.parse(json); result.__proto__ = source.__proto__; return result; };