寫一下這幾天在看面試題時又回顧到的一個知識點借卧,深淺拷貝。
這個應該是在ES5面向?qū)ο笾皩戇^敦姻,現(xiàn)在是忘的差不多了瘾境,在梳理了一下邏輯以后,決定重新開篇文章寫一下镰惦。
一迷守、堆與棧
如果是明白堆與棧的,或者是不屑于理解基礎知識旺入,只求深淺拷貝怎么寫的兑凿,可以跳過這部分直接看后面。但我認為理解一個東西茵瘾,就要貫徹到底礼华,一知半解多半會出問題。
那我們先說一下堆與棧拗秘,什么是堆什么是棧呢圣絮。在我們創(chuàng)建變量之初,內(nèi)存會開辟兩類空間雕旨,就是棧和堆扮匠。當然我們知道捧请,變量有兩種類型:基本數(shù)據(jù)類型(包括ES6的symbol數(shù)據(jù))和引用類型數(shù)據(jù),所以這兩種對象分別儲存于這兩個空間之中棒搜。
這樣應該很好理解疹蛉,那么堆與棧的關系呢?這里就要提到一個問題了力麸,在ECMAscript語法中可款,js是不允許直接訪問堆內(nèi)存的,那么我們要如何取出引用類數(shù)據(jù)呢末盔,這就要提到地址了筑舅。每個引用類數(shù)據(jù)座慰,都有對應的地址(地址不唯一)陨舱,每個地址都能指向?qū)亩褦?shù)據(jù)。舉個例子:
let demo = {id:"xiaoming"}
那么這行代碼中版仔,{id:"xiaoming"}就會儲存于內(nèi)存開辟的堆空間中游盲,而demo就會作為地址的變量名存放于棧內(nèi)存中。如果還沒看懂蛮粮,我畫個圖益缎,作為對比我再新建幾個變量:
let demo = {id:"xiaoming"};
let a = 'hello';
let b = 123
通過上述圖示,應該就能很清楚的展示基本數(shù)據(jù)類型和引用數(shù)據(jù)類型的存儲區(qū)別了变泄,這里再附上一段比較代碼令哟,大家參考一下,應該會更加清楚:
var str1 = new String('abc');
var str2='abc';
alert(str1==str2);// true
alert(str1===str2);// false
最后總結一下:
1.在代碼運行的時候妨蛹,每個線程會分配一個堆和一個棧屏富。堆的大小是不固定的,可以隨時增加蛙卤;而棧創(chuàng)建的時候就確定大小狠半,所以可能溢出。
2.堆的空間大颤难,但是運行效率相對較低神年;而棧相反。
3.棧存放基本類型數(shù)據(jù)行嗤,函數(shù)已日,對象指針等;堆存放對象昂验;
二捂敌、遞歸
同上艾扮,只想看深淺拷貝代碼的可以跳過了。但這里我還是建議看一下的占婉。
簡單來說泡嘴,遞歸是一種函數(shù)編碼思想。用法就是在函數(shù)內(nèi)調(diào)用自身逆济,同時為其中增加一個條件來終止代碼(不然會無限循環(huán))酌予。舉個例子:
var num = 0;
? ? ? ? function foo2(){
? ? ? ? ? ? num ++
? ? ? ? ? ? console.log("當前的num值:"+num);
? ? ? ? ? ? if(num > 5){
? ? ? ? ? ? ? ? // 終止代碼的執(zhí)行
? ? ? ? ? ? ? ? return ;
? ? ? ? ? ? }
? ? ? ? ? ? foo2();
? ? ? ? }
? ? ? ? foo2();
遞歸是一種理解起來并不難,但實際操作時卻需要想半天的東西奖慌,只能多寫來提高熟練度了抛虫。
三、淺拷貝
所謂拷貝简僧,說穿了就是數(shù)據(jù)的復制建椰,但在js中因為我前面提到的堆和棧的問題,我們?nèi)绻皇呛唵蔚难h(huán)岛马,僅僅只能對淺層的數(shù)據(jù)進行復制棉姐,這就是淺拷貝,那么在不涉及嵌套對象的前提下啦逆,就對僅僅是基本數(shù)據(jù)類型的對象進行拷貝伞矩。
懶人版淺拷貝方法一
此方法是ES6中提供的,但注意夏志,該方法不會拷貝不可枚舉的屬性和繼承屬性乃坤。
object.assign
?let demo = {};
? ? ? ? let test = {
? ? ? ? ? ? name:'aaa',
? ? ? ? ? ? age:123
? ? ? ? }
? ? ? ? Object.assign(demo,test);
? ? ? ? console.log(demo);
這里多一嘴:這種方式不止ES6,在jq中也有提供$.extend()方法沟蔑,效果是一樣的湿诊。
懶人版淺拷貝方法二
同樣是在ES6中,由語法糖提供的擴展運算符溉贿,這個就更簡單了枫吧。。
demo = {...test}
console.log(demo);
懶人版淺拷貝方法三
看了上面的宇色,覺得淺拷貝真的很簡單吧九杂,這里再說一下,同樣可以用slice宣蠕,concat等對數(shù)組進行操作(注意splice方法例隆,該方法會對原數(shù)組進行切剪)
js原生代碼的淺拷貝
function simpleCopy(data) {
? ? ? ? ? ? // 新地址, 判斷data是數(shù)組,還是對象
? ? ? ? ? ? var newData = Array.isArray(data)? [] : {}
? ? ? ? ? ? // 循環(huán)data ,然后復制數(shù)據(jù)
? ? ? ? ? ? for(var key in data){
? ? ? ? ? ? ? ? // 復制
? ? ? ? ? ? ? ? newData[key] = data[key]
? ? ? ? ? ? }
? ? ? ? ? ? // 返回復制完成的數(shù)據(jù)集合
? ? ? ? ? ? return newData;
? ? ? ? }
四抢蚀、深拷貝
終于到深拷貝了镀层,那么先說一下,深拷貝就不止是對淺層數(shù)據(jù)的拷貝了唱逢,我們要結合上面的堆棧和遞歸思想吴侦,實現(xiàn)原有對象的完全復制。二者實現(xiàn)真正的分離坞古。
懶人版深拷貝方法一
ok备韧,那么按照順序先放一個懶人版的,我們可以通過JSON來實現(xiàn)深淺拷貝(應該是目前最簡單的深淺拷貝了)痪枫。
遞歸深拷貝
這里提出三個問題奶陈,有待解決:
1.關于不可枚舉的屬性以及symbol類型數(shù)據(jù)的復制
2.Date易阳,RegExp類型
3.關于數(shù)據(jù)循環(huán)引用的問題?
我寫了一段吃粒,但是沒有完全解決潦俺,帶我搞定了上傳代碼