直接赋值
基本数据类型保存在栈里面,可以直接访问它的值,赋值时,会完整复制变量值。
let a = 10 let b = a a = 20 console.log(b)
|
引用数据类型保存在堆里面,栈里面保存的是地址,通过栈里面的地址去访问堆里面的值。
let obj1 = { a : 1 } let obj2 = obj1 obj1.a = 2 console.log(obj2.a)
|
这是直接赋值的情况,要想两个值互不影响,就需要进行拷贝,js中的拷贝分为浅拷贝和深拷贝
浅拷贝
我的理解就是,浅拷贝实际上只拷贝了一层
具体实现方法有这几种:
- 拓展运算符
let a = [1,2,3] let b = [...a] a[0] = 0 console.log(b)
|
- slice方法
let a = [1,2,3] let b = a.slice() a[0] = 4; console.log(b)
|
- assign方法
let obj1 = { a: 1, b: 2 } let obj2 = Object.assign({}, obj1) obj1.a = 5 console.log(obj2);
|
- 手搓
function shallowCopy (obj){ if(typeof obj !== 'object' || obj === null) { return obj; } let newObj = Array.isArray(obj) ? []: {}; for(let key in obj ){ if(obj.hasOwnProperty(key)){ newObj[key] = obj[key]; } } return newObj; }
|
但是如果有嵌套的话,浅拷贝就会出问题
let obj1 = { a: 1, b: { c: 2 } } let obj2 = Object.assign({}, obj1) obj1.b.c = 5 console.log(obj2);
|
因为这里复制的b的属性是引用类型,所以复制的还是它的内存地址
深拷贝
复制对象及其所有嵌套对象和数组,确保新对象与原对象完全独立
下面是具体实现方法:
1.使用JSON
let a = [ [1],[2], [3]] let b = JSON.parse(JSON.stringfy(a)); a[0][0] = 4; console.log(a) console.log(b)
|
利用JSON.stringify将对象序列化成为JSON字符串,并将对象里面的内容转换成字符串,再使用JSON.parse来反序列化,将字符串生成一个新的对象
这个方法虽然简单,但也存在一些问题:
- ⽆法解决循环引用问题
- 无法拷贝一些特殊的对象,如 RegExp (会变成空对象)、 Date (被转成字符串)
- 无法拷贝函数
- 无法拷贝undefined
2.使用第三方库 lodash
const _ = require('lodash'); let obj = { a: 1, b: { c: 2 } }; let deepCopy = _.cloneDeep(obj);
|
3.structuredClone
let a = [ [1],[2], [3]] let b = structuredClone(a); a[0][0] = 4; console.log(a) console.log(b)
|
这个是一个web api,只适用于在浏览器环境下使用
4.手搓(递归)
let newObj = {} function deepClone(newO,old){ for(let key in old) { let value = old[key] if(value instanceof Array) { newO[key] = [] deepClone(newO[key],value) }else if(value instanceof Object) { newO[key] = {} deepClone(newO[key],value) } }else { newO[key] = value } } deepClone(newObj,oldObj)
|
循环引用问题
let oneObj = { name: 'oneObj' } let twoObj = { name: 'twoObj', age: 15, family: oneObj } oneObj.a = twoObj
|
如果直接按照上面的递归来深拷贝,就会陷入无线循环
解决方法:ES6 的Map对象,它的key可以是任意类型(对象类型)
let newObj = {} function deepClone(newO,twoObj,map) { if(!map){ map = new Map() } for(let key in twoObj) { let value = twoObj[key] if(value instanceof Array) { newO[key] = [] deepClone(newO[key],value) }else if(value instanceof Object) { newO[key] = {} if(!map.has(value)) { map.set(value,1) deepClone(newO[key],value,map) } } else { newO[key] = value } } } deepClone(newObj,twoObj)
|