
今天來說說js中的深拷貝與淺拷貝,
首先來看看這兩個概念的意思:
淺拷貝的意思就是只復制參考(參考記憶體地址,所以改了被復制的物件的值,復制的值也會改變),沒有復制真正的值,
深拷貝就是指直接復制真正的值,不會隨著原來物件值的改變而改變,
不理解的可以看這個小demo:
String型別:
// 我們先申明一個變數str1,
// 然后把變數str1負值(拷貝)給變數str2
// 最后對變數str2進行修改操作
var str1 = 'lichen'
var str2 = str1
str2 += 'curry'
console.log('str1:', str1) //lichen
console.log('str2:', str2) //curry
參考物件型別:
var obj1 = {
name: 'lichen'
}
var obj2 = obj1
obj2.name = 'curry'
//期望輸出
// obj1: {name: "lichen"}
// obj2: {name: "curry"}
console.log('obj1:', obj1) // obj1: {name: "curry"}
console.log('obj2:', obj2) // obj2: {name: "curry"}
原因是因為String型別屬于基本資料型別,Object(Array)屬于參考資料型別,當我們申明一個基本型別并對它進行賦值的時候,計算機會將值保存在堆疊記憶體中,而當我們申明一個參考資料型別并對它進行賦值的時候,計算機會將值保存在堆記憶體中,參考型別變數其實就是一個指標指向堆記憶體中,如果復制兩相同的參考型別變數,其實它們最終指向同一個物件或者說堆記憶體空間,
JS中有字串(String)、數字(Number)、布爾(Boolean)、對空(Null)、未定義(Undefined)、Symbol、 參考資料型別這幾種資料型別,只有參考資料型別才會存在深拷貝與淺拷貝,參考資料型別包括物件(Object)、陣列(Array)、函式(Function)這三種
堆疊堆記憶體會在文章末尾解釋,可以先這樣理解:
堆疊區——由編譯器自動分配和釋放,一般存放函式的引數值、區域變數的值等(速度較快);堆區——由程式員分配及釋放,若程式員不釋放,程式結束后可能由OS回收(速度比較慢,而且容易產生記憶體碎片)
我們可以簡單地記住!復制傳值,參考傳址(記憶體地址)
實作方法
實作深拷貝可以使用JSON中的方法,第三方庫 jQuery.extend 和 lodash和自己手寫遞回方法等,
手寫遞回的實作的思路就是就是遍歷那個被拷貝的物件,判斷物件里每一項的資料型別,如果不是物件型別,就直接賦值,如果是物件型別,就再次呼叫deepCopy,遞回的去賦值
手寫遞回的實作方法:
function deepCopy(source) {
let target = Array.isArray(source) ? [] : {}
for (let k in source) {
if (typeof source[k] === 'object') {
target[k] = deepCopy(source[k])
} else {
target[k] = source[k]
}
}
return target
}
JSON的實作方法:
var obj1 = {
name: 'lichen'
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = 'curry'
console.log('obj1:', obj1) // obj1: {name: "lichen"}
console.log('obj2:', obj2) // obj2: {name: "curry"}
但是JSON這種方法有兩個缺點:
- 如果你的物件里有函式,函式無法被拷貝下來
- 無法拷貝copyObj物件原型鏈上的屬性和方法
JQuery的API:
$.extend( true, object1, object2 ); // 深度拷貝
$.extend( object1, object2 ); // 淺拷貝
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]); // => false
最后附上:
堆疊和堆都是記憶體的一部分,都可供你的代碼在runtime期間使用,但是他們是由不同的方式構建,堆疊按順序存盤值,并以相反的順序洗掉值,這被稱為后進先出(last in first out、LIFO),想象一堆盤子:當你加更多時候,你將他們放到堆疊的頂部,當你需要一個時候,你從頂部取走一個,并不能從中間和底部增加或拿走盤子,添加資料叫做壓堆疊(pushing onto the stack)洗掉資料叫做彈出堆疊(popping off the stack),所有存盤在堆疊上的資料必須已知固定大小,編譯時大小不確定或可能變更大小的必須存盤在堆上,堆的組織性差些,當你把data放到堆上時,你要需要一定空間,os會在堆上找到一塊足夠大的空區域,把它標記為在使用,并且回傳一個指標,該指標是這個位置的地址,這個程序稱為在堆上分配,或就縮寫為分配(allocating),將值放入堆疊中不被認為分配,因為指標都是已知的固定大小,所以可以將指標存盤在堆疊中,但當你需要實際資料時,必須遵循指標,想象一下,當你進入餐廳時,你會說出你們一群人的個數,然后作業人員會找到一個適合容納全部人的空桌,并且帶你們過去,如果組里的一些人來晚了,他們可以詢問在哪可以找到你們,在堆疊上分配記憶體比堆上更快,因為作業系統不需要為新的資料搜索一塊空間,位置始終位于堆疊的頂部,相比之下,在堆上分配,需要更多的作業,因為作業系統首先必須找到足夠大的空間來存盤資料,并且進行記錄,為下一次分配準備,訪問堆上的資料比訪問堆疊上的要慢,因為你必須根據指標找到那,如果現代處理器在記憶體上跳的少,它們會更快,繼續類比,設想餐廳里的一個服務員收到許多桌子的訂單,最高效的方法是在去下一張桌子前在一張桌上得到所有訂單,從A桌拿到一份訂單,然后從B桌,然后又是A桌,然后又是B桌,會處理的慢得多,出于同樣的原因,如果處理的資料靠近其他資料(在堆疊上)而不是更遠(在堆上),處理器可以更快的完成任務,且在堆上分配大量空間也需要時間,當你的代碼呼叫一個函式時,傳遞給函式的值(可能包括堆上指向資料的指標)和函式的區域變數會壓入堆疊中,當函式結束,這些值將從堆疊中彈出,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/231026.html
標籤:其他
上一篇:CSDN開發者周刊第19 期:GitHub 2020 年度報告 TypeScript 超越 C#、PHP 和 C++;Kubernetes 棄用 Docker!
下一篇:二叉樹家譜關系實驗報告
