1.前言
关于浅拷贝和深拷贝,我们首先要明确什么时候使用,所以每次变量对变量进行赋值的时候我们都要考虑当前要使用深拷贝还是浅拷贝,不然会带来不易发现的BUG。
2.浅拷贝和深拷贝
首先我们要了解,其实所有的拷贝都只会拷贝栈(stack)中的数据或堆地址,所以要知道js中主要两类数据类型,一种就是基本数据类型,还有一种就是引用数据类型,它们分别在栈(stack)中存储的是什么?
2.1基本数据类型
基本数据类型中有string,number,boolean,null,undefined,他们栈(stack)中存的是实际数据,所以基本数据类型每次拷贝相当于在栈(stack)中添加了一个独立的新数据,所以不存在浅拷贝。
2.2 引用数据类型
引用数据类型主要有Array,Object,function,他们在栈(stack)中只会存储堆地址,它们实际数据存在堆当中,所以当拷贝的时候变量只拷贝栈(stack)中存放堆地址,两个变量会同时指向同一个堆数据,而深拷贝变量会在栈(stack)中存放新的堆地址,同时增加一个新的堆数据
3.Vue-TypeScript中数据类型的深浅拷贝
3.1响应式基本数据类型
先新建两个值
let num1: Ref<number> = ref(0);
let num2: Ref<number> = num1;
上面说过基本数据类型直接拷贝就是深拷贝。所以上面的代码对吗?
我们添加两个方法试试便知
const numUp = () => {
num1.value++;
};
const numDown = () => {
num1.value--;
};
这里两个方法只会改变num1的值
这里是运行界面num1和num2都为0没错,然后我们加一个试试
我们发现为什么数据值同时改变了,我们打印num2一下看看。
console.log(num2);
console.log(typeof num2);
我们发现num2已经不是基本数据类型了,其实是因为我们响应式这个基本数据类型了,使用ref()方法是会返回一个Object对象也就是ref对象,所以他已经不是一个基本数据类型了。所以num2只是对num1的浅拷贝。如果要深拷贝我们就要获得他的真实数据,vue提供了.value方法可以获取到真实数据,所以换一种写法
let num1: Ref<number> = ref(0);
let num2: Ref<number> = ref(num1.value);
所有的基础类型深拷贝都要这样写
3.2 响应式引用数据类型
浅拷贝,修改值得时候引用info1和info2的地方都会发生改变
import { reactive } from "vue";
interface Info {
info?: string;
}
let info1: Info = reactive({
info: "信息",
});
let info2: Info = info1;
深拷贝
1. JSON.stringify()和JSON.parse()
Object和Array类型都可以使用
import { reactive } from "vue";
interface Info {
info?: string;
}
let info1: Info = reactive({
info: "信息",
});
//深拷贝
let info2: Info = reactive(JSON.parse(JSON.stringify(info1)));
可以理解为修改成string类型,然后在转换为Object,这样就会重新分配栈堆
2. 递归
Object
递归只会对第一层深拷贝,如果存在属性为Object或Array类型的话会导致浅拷贝,所以我们要层级递归。
interface user {
name: string;
age: number;
}
interface Info {
info?: string;
userInfo?: user;
}
let info1: Info = reactive({
info: "信息",
userInfo: {
name: "名字",
age: 18,
},
});
let info2: Info = {};
const deepClone = (obj: Info): Info => {
let result = {};
if (obj && typeof obj === "object") {
for (let key in obj) {
// 判断是否是数组
if (Array.isArray(obj[key])) {
let arr = [];
for (let i = 0; i < obj[key].length; i++) {
arr.push(deepClone(obj[key][i]));
}
result[key] = arr;
} else if (obj[key] && typeof obj[key] === "object") {
result[key] = deepClone(obj[key]); //如果对象的属性值为object的时候,递归调用deepClone
} else {
result[key] = obj[key];
}
}
return result;
}
return obj;
};
info2 = reactive(deepClone(info1));
Array
array同理只要存在属性为Object或Array类型都需要层级递归
const deepClone = (obj: Info): Info => {
let result = {};
if (obj && typeof obj === "object") {
for (let key in obj) {
// 判断是否是数组
if (Array.isArray(obj[key])) {
let arr = [];
for (let i = 0; i < obj[key].length; i++) {
arr.push(deepClone(obj[key][i]));
}
result[key] = arr;
} else if (obj[key] && typeof obj[key] === "object") {
result[key] = deepClone(obj[key]); //如果对象的属性值为object的时候,递归调用deepClone
} else {
result[key] = obj[key];
}
}
return result;
}
return obj;
};
let array1: Info[] = reactive([
{
info: "信息1",
userInfo: {
name: "名字1",
age: [18, 19, 20],
},
},
{
info: "信息2",
userInfo: {
name: "名字2",
age: [18, 19, 20],
},
},
{
info: "信息3",
userInfo: {
name: "名字3",
age: [18, 19, 20],
},
},
]);
let array2: Info[] = reactive([]);
for (let i = 0; i < array1.length; i++) {
array2.push(deepClone(array1[i]));
}
还有几种只会深拷贝基本数据类型的方法,后面在更。。。