您好,登錄后才能下訂單哦!
這篇文章給大家介紹JS中實現淺拷貝和深拷貝,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
(一)JS中基本類型和引用類型
JavaScript的變量中包含兩種類型的值:基本類型值 和 引用類型值,在內存中的表現形式在于:前者是存儲在棧中的一些簡單的數據段,后者則是保存在堆內存中的一個對象。
基本類型值
在JavaScript中基本數據類型有 String , Number , Undefined , Null , Boolean ,在ES6中,又定義了一種新的基本數據類型 Symbol ,所以一共有6種。
基本類型是按值訪問的,從一個變量復制基本類型的值到另一個變量后,這兩個變量的值是完全獨立的,即使一個變量改變了也不會影響到第二個變量。
var str1 = '撩課'; var str2 = str1; str2 = 'itlike'; console.log(str2); //'itlike' console.log(str1); //'撩課'
引用類型值
引用類型值是引用類型的實例,它是保存在堆內存中的一個對象,引用類型是一種數據結構,最常用的是Object,Array,Function類型,此外還有Date,RegExp,Error等。
在ES6中提供了Set,Map2種新的數據結構。
(二)JS中如何復制引用類型的
基本類型和引用類型賦值的差異化
舉個例子:在下面代碼中,只修改了obj1中的name屬性,卻同時改變了ob1和obj2中的name屬性。
var obj1 = {'name': '撩課'}; var obj2 = obj1; obj2.name = '小撩'; console.log(obj1); // {'name': '撩課'} console.log(obj2); // {'name': '撩課'}
當變量復制引用類型值的時候,同樣和基本類型值一樣會將變量的值復制到新變量上,不同的是對于變量的值,它是一個指針,指向存儲在堆內存中的對象。
因為,在JS中,堆內存中的對象無法直接訪問,必須要訪問這個對象在堆內存中的地址,然后再按照這個地址去獲得這個對象中的值。
(三)淺拷貝
在JS中,如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是引用類型,拷貝的就是內存地址;所以如果其中一個對象改變了這個地址,就會影響到另一個對象。
下面是JavaScript提供的淺拷貝方法:
Object.assign
ES6中拷貝對象的方法,接受的第一個參數是拷貝的目標,剩下的參數是拷貝的源對象;
語法:Object.assign(target, ...sources)
var p = { 'name': '張三', }; var copyP = {}; Object.assign(copyP, p); console.log(copyP); console.log(p);
Object.assign是一個淺拷貝,它只是在根屬性(對象的第一層級)創建了一個新的對象,但是如果屬性的值是對象的話,只會拷貝一份相同的內存地址。
擴展運算符
利用擴展運算符可以在構造字面量對象時,進行克隆或者屬性拷貝。語法如下:
var cloneObj = { ...obj }; var obj = {'name': '撩課', 'college': ['H5','JAVA','Python']} var obj2 = {...obj}; obj.name='小撩'; //{'name': '小撩', 'college': ['H5','JAVA','Python']} console.log(obj); //{'name': '撩課', 'college': ['H5','JAVA','Python']} console.log(obj2); obj.college.push('Go'); //{'name': '小撩', 'college': ['H5','JAVA','Python','Go']} console.log(obj); //{'name': '小撩', 'college': ['H5','JAVA','Python','Go']} console.log(obj2);
擴展運算符和Object.assign()存在同樣的問題,對于值是對象的屬性無法完全拷貝成兩個不同對象;
但是如果屬性都是基本類型的值的話,使用擴展運算符更加簡潔。
(四)深拷貝
淺拷貝只在根屬性上在堆內存中創建了一個新的的對象,復制了基本類型的值,但是復雜數據類型也就是對象則是拷貝相同的地址。
而深拷貝則是將一個對象從內存中完整的拷貝一份出來,從堆內存中開辟一個新的區域存放新對象,且修改新對象不會影響原對象。
JSON.stringify
JSON.stringify()是目前開發過程中最常用的深拷貝方式,原理是把一個對象序列化成為一個JSON字符串,將對象的內容轉換成字符串的形式再保存在內存中,再用JSON.parse()反序列化將JSON字符串變成一個新的對象。
舉個例子:
var obj = { name: '撩課', age: 18, friends: ['小花', '小黑'], goodF: { name: '小撩', age: 19, address: '上海', pets: [{name: '土豆'}, {name: '馬鈴薯'}]}, bir: new Date() }; var newObj = JSON.parse(JSON.stringify(obj)); obj.goodF.pets[0].name = '旺財'; console.log(newObj); console.log(obj);
使用JSON.stringify實現深拷貝有幾點要注意:
1)拷貝的對象的值中如果有函數,undefined,symbol,經過JSON.stringify()序列化后的JSON字符串中這個鍵值對會消失;
無法拷貝不可枚舉的屬性,無法拷貝對象的原型鏈
3)拷貝Date引用類型會變成字符串
4)拷貝RegExp引用類型會變成空對象
對象中含有NaN、Infinity和-Infinity,則序列化的結果會變成null
遞歸實現深拷貝
具體實現如下:
/** * 輔助函數, 判定是否是對象 * @param obj * @returns {boolean} */ function isObj(obj) { return obj instanceof Object; } /** * 深拷貝fromObj面的所有屬性/值, 到toObj對象里面 * @param fromObj 拷貝對象 * @param toObj 目標對象 */ function deepCopyObj2NewObj(fromObj, toObj) { for (var key in fromObj) { if(fromObj.hasOwnProperty(key)){ var fromValue = fromObj[key]; // 如果是值類型,那么就直接拷貝賦值 if (!isObj(fromValue)) { toObj[key] = fromValue; } else { // 如果是引用類型,那么就再調用一次這個方法, // 去內部拷貝這個對象的所有屬性 // fromValue是什么類型, 創建一個該類型的空對象 var tmpObj = new fromValue.constructor; // console.log(tmpObj); // debugger; deepCopyObj2NewObj(fromValue, tmpObj); toObj[key] = tmpObj; } } } }
關于JS中實現淺拷貝和深拷貝就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。