深拷貝和淺拷貝詳解
文章目錄
一.概念
其實一般來說,我們遇到深淺拷貝的問題,都是針對引用數據類型的變量操作.先了解下數據類型
1.值類型和引用類型
值類型:直接存儲其值,在內存中,是存在棧內存中
引用類型: 存儲對值的引用,在內存中,是存在堆內存中,變量本身僅僅是一個指向堆中的實際數據地址,存在棧內存中(說白了,就是引用數據類型,實際上存在堆內存中,雜亂無序的放着,但是會有一個指針,指向堆內存中的地址, 而這個指針則是在棧內存中存儲)
1.1值類型
- Boolean
- String
- Undefined
- Null (typeof 去判斷時,會是object,因爲null被轉換成機器語言時很多個0,而只要前三個爲0的話,就會被機器語言判斷爲object)
- Number
- Symbol(ES6 唯一性)
1.2 引用類型
- Object
- Array
- Function
- RegExp
- Date
- Function
1.淺拷貝
淺拷貝拷貝的是對象的指針,沒有新建空間地址,新老對象共用的同一份地址,指向對內存中同一個地方,修改原來的對象會影響新對象
var obj1={
a:1
}
var obj2=obj1;
obj2.a=5;
console.log('obj1.a',obj1.a); //5
console.log('obj2.a',obj2.a); //5
上述代碼,就是一個淺拷貝,對象通過等於號直接賦值,是一個淺拷貝,obj1對象賦值給了obj2,實際上obj2只是拷貝了obj1在內存中棧中的一個地址,也就是指針,他們倆會共同指向堆中數據
2.深拷貝
深拷貝會在內存中,開闢一個相同的空間地址,並且複製相同的數值,兩者互不干擾
2.1 JSON.parse+JSON.stringify
用JSON.stringify把對象轉爲字符串,再用JSON.parse把字符串轉爲新的對象.
var obj3 = {
classId: 2001,
userinfo: {
name: 'liuqiao',
age: '27'
}
}
var obj4 = JSON.parse(JSON.stringify(obj3));
obj4.userinfo.name='zhangsan';
console.log(obj3.userinfo.name); //liuqiao
console.log(obj4.userinfo.name); //zhangsan
侷限性:Number,String,Boolean,Array,可以轉爲JSON對象,但是function這種不行
2.2 lodash 實現深拷貝
lodash是一個高性能的 JavaScript 實用工具庫,裏面的lodash.cloneDeep()實現深拷貝,原理也是遞歸實現的
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
var obj3 = {
classId: 2001,
userinfo: {
name: 'liuqiao',
age: '27'
}
}
var obj4 = _.cloneDeep(obj3);
obj4.userinfo.name = 'zhangsan';
console.log(obj3.userinfo.name); //liuqiao
console.log(obj4.userinfo.name); //zhangsan
</script>
2.3 遞歸拷貝
實現原理就是通過遞歸,循環遍歷從最頂級開始,去找下一級,直到最後一層位置,然後賦值
function deepClone(source) {
// 判斷複製的目標是數組還是對象
const targetObj = source.constructor === Array ? [] : {};
for (let keys in source) { // 遍歷目標
if (source.hasOwnProperty(keys)) {
// 如果值是對象,就遞歸一下
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
} else {
// 如果不是,就直接賦值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
var obj3 = {
classId: 2001,
userinfo: {
name: 'liuqiao',
age: '27'
}
}
var obj4 = deepClone(obj3);
obj4.userinfo.name = 'zhangsan';
console.log(obj3.userinfo.name); //liuqiao
console.log(obj4.userinfo.name); //zhangsan
2.4 Object.assign()
實際上Object.assign是一個淺拷貝,但是如果是數據結構只有一層,可以實現深拷貝
var obj6={};
var obj5={
a:1
}
obj6= Object.assign(obj6,obj3);
obj6.a=2;
console.log(obj6.a); //2
console.log(obj5.a); //1
Object.assign,如果數據結構有多層,則是淺拷貝
var obj3 = {
classId: 2001,
userinfo: {
name: 'liuqiao',
age: '27'
}
}
var obj4={};
obj4= Object.assign({},obj3);
obj4.userinfo.name = 'zhangsan';
console.log(obj3.userinfo.name); //zhangsan
console.log(obj4.userinfo.name); //zhangsan
2.5 ES6擴展運算符 …
與Object.assign() 一樣,如果數據結構只有一層,是深拷貝,多層結構了是淺拷貝,只能拷貝第一層的
擴展運算符淺拷貝
var obj3 = {
classId: 2001,
userinfo: {
name: 'liuqiao',
age: '27'
}
}
obj4 = { ...obj3 }
obj4.userinfo.name = 'zhangsan';
obj4.classId=3004;
console.log(obj3.userinfo.name); //zhangsan
console.log(obj4.userinfo.name); //zhangsan
console.log(obj3.classId); //2001
console.log(obj4.classId); //3004
擴展運算符深拷貝
var obj6 = {};
var obj5 = {
a: 1
}
obj6 = { ...obj5 };
obj6.a = 2;
console.log(obj6.a); //2
console.log(obj5.a); //1
二.總結
實際項目中,根據不同的業務場景,判斷是否需要使用深拷貝,方式有很多種,根據需求來自己實現即可!