javascript深淺拷貝

underscore 的源碼中,有很多地方用到了 Array.prototype.slice() 方法,但是并沒有傳參,實際上只是為了返回數組的副本,例如 underscore 中 clone 的方法:

// Create a (shallow-cloned) duplicate of an object.// 對象的 `淺復制` 副本// 注意點:所有嵌套的對象或者數組都會跟原對象用同一個引用// 所以是為淺復制,而不是深度克隆_.clone=function(obj){// 容錯,如果不是對象或者數組類型,則可以直接返回// 因為一些基礎類型是直接按值傳遞的if(!_.isObject(obj))returnobj;// 如果是數組,則用 obj.slice() 返回數組副本// 如果是對象,則提取所有 obj 的鍵值對覆蓋空對象,返回return_.isArray(obj)?obj.slice():_.extend({},obj);};

這里就涉及到了一個知識點:深淺拷貝。

所謂深淺拷貝,都是進行復制,那么區別主要在于復制出來的新對象和原來的對象是否會互相影響,改一個,另一個也會變。

淺拷貝栗子:

vara=["a","b","c"];vara_slice=a;console.log(a===a_slice);a_slice[0]="f";console.log(a_slice);console.log(a);

深拷貝栗子:

varobj=[[1,2,3],4,5];varobj_extend=$.extend(true,{},obj);//extend方法,第一個參數為true,為深拷貝,為false,或者沒有為淺拷貝。console.log(obj===obj_extend);obj[0][0]="heihei";console.log(obj);console.log(obj_extend);

有了上面的大概認識,讓我們從原理深入了解一下深淺拷貝。

一、基本類型 和 引用類型

1、ECMAScript 中的變量類型分為兩類:

基本類型:undefined,null,布爾值(Boolean),字符串(String),數值(Number)

引用類型: 統稱為Object類型,細分的話,有:Object類型,Array類型,Date類型,Function類型等。

2、不同類型的存儲方式:

基本數據類型保存在棧內存,形式如下:棧內存中分別存儲著變量的標識符以及變量的值。

vara="A";

在棧內存中是這樣的

引用類型保存在堆內存棧內存存儲的是變量的標識符以及對象在堆內存中的存儲地址,當需要訪問引用類型(如對象,數組等)的值時,首先從棧中獲得該對象的地址指針,然后再從對應的堆內存中取得所需的數據。

var a = {name:“jack”};

在內存中是這樣的

3、不同類型的復制方式:

基本類型的復制:當你在復制基本類型的時候,相當于把值也一并復制給了新的變量。

栗子 1:

vara=1;varb=a;console.log(a===b);vara=2;console.log(a);console.log(b);

改變 a 變量的值,并不會影響 b 的值。

內存中是這樣的:

vara=1;

var b = a;

a = 2;

引用類型的復制:當你在復制引用類型的時候,實際上只是復制了指向堆內存的地址,即原來的變量與復制的新變量指向了同一個東西。

栗子 2:

vara={name:"jack",age:20};varb=a;console.log(a===b);a.age=30;console.log(a);console.log(b);

改變 a 變量的值,會影響 b 的值。

內存中是這樣的:

var a = {name:"jack",age:20};

var b = a;

a.age = 30;

二、明白了上面之后,所謂深淺拷貝

對于僅僅是復制了引用(地址),換句話說,復制了之后,原來的變量和新的變量指向同一個東西,彼此之間的操作會互相影響,為淺拷貝

而如果是在堆中重新分配內存,擁有不同的地址,但是值是一樣的,復制后的對象與原來的對象是完全隔離,互不影響,為深拷貝

深淺拷貝的主要區別就是:復制的是引用(地址)還是復制的是實例。

所以上面的栗子2,如何可以變成深拷貝呢?

我們可以想象出讓 b 在內存中像下圖這樣,肯定就是深拷貝了。

那么代碼上如何實現呢?

利用遞歸來實現深復制,對屬性中所有引用類型的值,遍歷到是基本類型的值為止。

functiondeepClone(source){if(!source&&typeofsource!=='object'){thrownewError('error arguments','shallowClone');}vartargetObj=Array.isArray(source)?[]:{};for(varkeysinsource){if(source.hasOwnProperty(keys)){if(source[keys]&&typeofsource[keys]==='object'){targetObj[keys]=deepClone(source[keys]);//遞歸}else{targetObj[keys]=source[keys];}}}returntargetObj;}

檢測一下

vara={name:"jack",age:20};varb=deepClone(a);console.log(a===b);a.age=30;console.log(a);console.log(b);

三 、最后讓我們來看看 一些 js 中的 復制方法,他們到底是深拷貝還是淺拷貝?

1、 Array 的 slice 和 concat 方法

兩者都會返回一個新的數組實例。

栗子:

slice:

vara=[1,2,3];varb=a.slice();//sliceconsole.log(b===a);a[0]=4;console.log(a);console.log(b);

concat:

vara=[1,2,3];varb=a.concat();//concatconsole.log(b===a);a[0]=4;console.log(a);console.log(b);

看到結果,如果你覺得,這兩個方法是深復制,那就恭喜你跳進了坑里

讓咱們再看一個顛覆你觀念的栗子:

vara=[[1,2,3],4,5];varb=a.slice();console.log(a===b);a[0][0]=6;console.log(a);console.log(b);

看見了嗎?都變啦!

這就是坑,知道嗎?

所以你要記住的是 Array的 slice 和 concat 方法 并不是真正的深拷貝,他們其實是披著羊(qian)皮(kao)的(bei)狼。

2、 jQuery中的 extend 復制方法

可以用來擴展對象,這個方法可以傳入一個參數:deep(true or false),表示是否執行深復制(如果是深復制則會執行遞歸復制)。

栗子:

深拷貝:

varobj={name:'xixi',age:20,company:{name:'騰訊',address:'深圳'}};varobj_extend=$.extend(true,{},obj);//extend方法,第一個參數為true,為深拷貝,為false,或者沒有為淺拷貝。console.log(obj===obj_extend);obj.company.name="ali";obj.name="hei";console.log(obj);console.log(obj_extend);

淺拷貝:

varobj={name:"xixi",age:20};varobj_extend=$.extend(false,{},obj);//extend方法,第一個參數為true,為深拷貝,為false,或者沒有為淺拷貝。console.log(obj===obj_extend);obj.name="heihei";console.log(obj);console.log(obj_extend);

咦,company 的變化 可以看出 深淺復制來(即箭頭所指), 紅色方框圈出的地方,怎么和上面 slice 和 concat 的情況一樣?難道也是羊?

其實總結一下就是:

Array 的 slice 和 concat 方法 和 jQuery 中的 extend 復制方法,他們都會復制第一層的值,對于第一層的值都是深拷貝,而到第二層的時候 Array 的 slice 和 concat 方法就是復制引用,jQuery 中的 extend 復制方法 則取決于 你的 第一個參數, 也就是是否進行遞歸復制。所謂第一層 就是 key 所對應的 value 值是基本數據類型,也就像上面栗子中的name、age,而對于 value 值是引用類型 則為第二層,也就像上面栗子中的 company。

3、JSON 對象的 parse 和 stringify

JOSN 對象中的 stringify 可以把一個 js 對象序列化為一個 JSON 字符串,parse 可以把 JSON 字符串反序列化為一個 js 對象,這兩個方法實現的是深拷貝。

栗子:

var obj = {name:'xixi',age:20,company : { name : '騰訊', address : '深圳'} };var obj_json = JSON.parse(JSON.stringify(obj));console.log(obj === obj_json);obj.company.name = "ali";obj.name = "hei";console.log(obj);console.log(obj_json);

完全的 深拷貝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容

  • 淺拷貝 1.基本數據類型 是存在棧中的,所以=賦值,都會創建一個新的空間,例如 變量b有自己獨立的空間 2.對象數...
    Addy_Zhou閱讀 264評論 0 0
  • 簡單講呢,深淺拷貝,都是進行復制,那么區別主要在于復制出來的新對象和原來的對象是否會互相影響,改一個,另一個也會變...
    _千尋瀑_閱讀 255評論 0 2
  • 最近寫一個項目用到了深淺拷貝,為了更深入的了解,又查了一些資料,找到了司徒正美大神的一個講解,非常好,這里分享一下...
    MakingChoice閱讀 226評論 0 0
  • 在 JS 中有一些基本類型像是Number、String、Boolean,而對象就是像這樣的東西{ name: '...
    tobAlier閱讀 577評論 0 0
  • 我要。。我要一杯溫水。 男人獨自坐在咖啡館的陰暗角落,小口啜飲著杯碟里的溫開水,呆滯動作,讓人一眼可知他心思渺遠。...
    季歡閱讀 310評論 0 3