前端面試-深拷貝和淺拷貝

2020-12-11 Tingno記

面試題目:如何實現對一個數組或對象的淺拷貝和深拷貝?

WTF,複製還分兩種,第一次遇到這種問題的時候很是無語呢,先來看看一般的答案的理解。

淺拷貝是只拷貝一層,深層次的對象級別就只拷貝引用。 深拷貝是拷貝多層,每一級別的數據都拷貝出來。也就是說,基本數據類型其實不存在深淺拷貝的問題,只有對象和數組才存在深淺拷貝的問題。

主要解決的是什麼問題呢?你去買房子,看中一套不錯要了,然後中介給你列印了一份合同,你籤字付錢。過一段時間去看,哎呀我去,怎麼裝修了?另外一個人也拿著同樣有合同、付款憑證。我以為是我買的房子,結果中介一房兩賣,別人也能搞。這怎麼行?

JS數據類型

js分為基本數據類型和複雜數據類型。

基本數據類型包括:String、Number、Boolean、Null、Undefined

複雜(引用)類型包括:Object、Array、Function

在開發過程中,經常使用 typeof 來檢測數據類型。默認var聲明的時候,如果不進行賦值,類型就是undefined。布爾值是boolean只有 true,false兩種值。

聲明的時候用的null,這時候代表空對象,使用typeof檢測的時候,顯示是Object。

JS內存管理

JS代碼運行的時候,數據都要寫入內存進行調用的,而不同的數據類型在內存中存放的方式是不一樣的。

基本數據類型是存儲在棧數據空間中,複雜數據類型是存儲在堆數據空間中的,而對數據空間不能直接訪問,需要棧這邊進行位置指引。

一個不是很恰當的比喻就是內存相當於倉庫。

倉庫裡面分了兩個區域,一邊是都是小格子,另一邊都是大貨櫃。簡單數據類型比如你的一本書,你的一份帳單什麼的就直接放在小格子裡面就好了。

另外你有一屋子書和一屋子的帳單,小格子放不下。你就租了一個小格子和一個倉庫。小格子裡面放著倉庫的鑰匙和倉庫的位置,倉庫裡面放東西。

實際的內存讀取方式也類似。要找自己的小格子,你就要從上到下挨著找。想要找自己貨櫃裡面的東西,還是需要先去小格子裡面找到存放鑰匙和位置的格子,找到以後直接去找貨櫃。

下圖是

淺拷貝和深拷貝

再沒有了解到深拷貝和淺拷貝知識的時候,一般拷貝就是從新賦值。聲明個數據直接用另外一個對象賦值。這個就算是淺拷貝。

當遇到複雜對象的時候,複製的只是對象的指針,並沒有重新開闢大的空間進行複製。這時候造成的影響就是對兩個指針進行數據操作的時候,操作的是同一個數據內容,相互之間是受影響的。

而深拷貝就是需要連指針到內容都進行複製,兩個指針指向兩個空間的內容。各自操作已經不受影響。

淺拷貝的實現方式

淺拷貝的複製就是直接複製賦值就可以了。

方法一:

function simpleClone(initalObj) { var obj = {}; for ( var i in initalObj) { obj[i] = initalObj[i]; } return obj;}var obj = { b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world"); }}var cloneObj = simpleClone(obj); console.log(cloneObj.b); console.log(cloneObj.c);console.log(cloneObj.d);cloneObj.b.a = "changed";cloneObj.c = [1, 2, 3];cloneObj.d = function() { alert("changed"); };console.log(obj.b);console.log(obj.c);console.log(obj.d);自行運行查看下變化及原因。

方法二: Object.assign是ES6的新函數。Object.assign() 方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然後返回目標對象。但是 Object.assign() 進行的是淺拷貝,拷貝的是對象的屬性的引用,而不是對象本身。

Object.assign(target, ...sources)var obj = { a: {a: "hello", b: 21} };var initalObj = Object.assign({}, obj);initalObj.a.a = "changed";console.log(obj.a.a); // "changed"需要注意的是:

Object.assign()可以處理一層的深度拷貝,如下:

var obj1 = { a: 10, b: 20, c: 30 };var obj2 = Object.assign({}, obj1);obj2.b = 100;console.log(obj1);// { a: 10, b: 20, c: 30 } <-- 沒被改到console.log(obj2);// { a: 10, b: 100, c: 30 }深拷貝的實現方式

如果要複製的對象只有一層,對象裡面的元素全是基本元素的話,前面的淺拷貝案例其實就完成了深拷貝的功能。我們重點說一下多層對象。

1.通過JSON轉換 用JSON.stringify把對象轉成字符串,再用JSON.parse把字符串轉成新的對象。

但是這種方法也有不少壞處,譬如它會拋棄對象的constructor。也就是深拷貝之後,不管這個對象原來的構造函數是什麼,在深拷貝之後都會變成Object。並且只能處理常見的Number, String, Boolean, Array, 扁平對象等這些能被JSON表示的數據結構。RegExp對象是無法通過這種方式深拷貝。

2.遞歸拷貝

function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : {}; arguments.callee(prop, obj[i]); } else { obj[i] = prop; } } return obj;}遞歸拷貝就是將對象逐層解開進行剖析,逐層新建對象,逐層複製,知道最深處的所有簡單數據都複製上。

但是要注意要注意對象引用對象的情況,會掉入死循環。

3.使用Object.create()方法 直接使用var newObj = Object.create(oldObj),可以達到深拷貝的效果。

function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用對象導致死循環,如initalObj.a = initalObj的情況 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj;}小案例

之前在進行公司VUE項目開發的過程中,由於需要將富文本編輯器抽離成為一個單獨的組件,然後將內容的對象傳入進去。如果按照傳統的vue組件開發的流程,肯定是要接收傳入、賦值給本組件、本組件編輯器修改、修改完畢的內容再進行emit外傳,然後組件外部接受,進一步處理。

但是由於vue組件之間淺拷貝的特性,其實傳入的對象修改之後,外部組件直接取值拿到的就是最新的值。

也是因為這個發現才對深拷貝和淺拷貝有了更加深入的了解。

相關焦點

  • 一文讀懂 javascript 深拷貝與淺拷貝
    讀完本文,希望你能明白:淺拷貝與深拷貝淺拷貝是創建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值,如果屬性是引用類型,拷貝的就是內存地址 ,所以如果其中一個對象改變了這個地址,就會影響到另一個對象。
  • 面試題-python 淺拷貝和深拷貝(copy模塊)
    前言面試的時候經常會問到深拷貝和淺拷貝,那麼python的深拷貝和淺拷貝有什麼區別呢?:list、dict 和 set集合copy模塊python 中的深拷貝和淺拷貝使用 copy 模塊淺拷貝 A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in
  • Python深拷貝和淺拷貝詳解
    True此程序中,元組 (1, 2, 3) 只被創建一次,t1 和 t2 同時指向這個元組。看到這裡,也許你可能對淺拷貝有了初步的認識。淺拷貝,指的是重新分配一塊內存,創建一個新的對象,但裡面的元素是原對象中各個子對象的引用。
  • 深拷貝和淺拷貝之list、dataframe
    python list:b = a是淺拷貝,b = list(a)和b = copy.cpoy(a)是深拷貝。淺拷貝,a和b指向的是一個地址。當b改變後,a也會改變。深拷貝,a和b指向的是兩個地址,當b改變後,a不受影響。
  • 5 張圖徹底理解 Python 中的淺拷貝與深拷貝
    Python 開發崗,面試官如果對基礎比較看重的話,那麼很可能會問你這樣的問題「談談你對 Python 中的淺拷貝和深拷貝的理解?」若平時你在開發中過度使用 deepcopy,以至於忘記了淺拷貝(shallow copy)和深拷貝(deep copy)的區別,那很可能要栽大跟頭了。本文轉載一篇小吉大佬的文章,幫助大家更好的理解深拷貝和淺拷貝的區別。
  • JavaScript系列--如何優雅簡單的實現深拷貝和淺拷貝
    優雅簡單的實現深拷貝和淺拷貝淺析JavaScript解析賦值、淺拷貝和深拷貝的區別:裡面介紹了解析賦值,淺拷貝,深拷貝的原理和實現。淺拷貝方法:Object.assign(),展開語法Spread,Array.prototype.alice(),array.prototype.concat()。深拷貝方法:JSON.parse(JSON.stringify(object)),對於undefined,symbol和函數的會直接忽略。
  • 看看你知道的「淺拷貝」是對的嗎
    ,詢問應聘者關於淺拷貝的知識後,在應聘者的回答中,筆者發現有好一部分人對淺拷貝都是錯誤的,故有了此篇內容。她: 「複製對象有深拷貝和淺拷貝...」大佬:」說一下這兩者之間的區別「她: 」我給你寫一段淺拷貝的代碼「var a = { x: 1 };var b = a;大佬:」回去等通知吧 ~.~「2.
  • 低門檻徹底理解JavaScript中的深拷貝和淺拷貝
    在說深拷貝與淺拷貝前,我們先看兩個簡單的案例:按照常規思維,obj1應該和num1一樣,不會因為另外一個值的改變而改變,而這裡的obj1 卻隨著obj2的改變而改變了。同樣是變量,為什麼表現不一樣呢?案例2中的賦值就是典型的淺拷貝,並且深拷貝與淺拷貝的概念只存在於引用類型。深拷貝與淺拷貝既然已經知道了深拷貝與淺拷貝的來由,那麼該如何實現深拷貝?
  • 面試題:如何實現一個深拷貝
    (給前端大全加星標,提升前端技能)作者:木易楊 (本文來自作者投稿)引言上篇文章詳細介紹了淺拷貝 Object.assign
  • JavaScript的深拷貝實現
    在實際開發當中,我們經常會遇到要對對象進行深拷貝的情況。而且深拷貝這個問題在面試過程中也經常會遇到,下面就對本人在學習過程中的收穫,做以簡單的總結。什麼是淺拷貝,什麼是深拷貝?什麼是淺拷貝關於淺拷貝的概念,我在網上看到一種說法,直接上代碼。
  • C++之拷貝構造函數的淺copy和深copy
    一、深拷貝和淺拷貝構造函數總結:1、兩個特殊的構造函數:(1)無參構造函數::(1)淺拷貝拷貝後對象的物理狀態相同(2)深拷貝拷貝後對象的邏輯狀態相同5、什麼時候需要進行深拷貝?(也就是我們自己手寫的),必然需要實現深拷貝!
  • 深拷貝的終極探索(90%的人都不了解)
    ,可以考察面試者的很多方面,比如基本功,代碼能力,邏輯能力,而且進可攻,退可守,針對不同級別的人可以考察不同難度,比如漂亮妹子就出1☆題,(*^__^*) 嘻嘻……一般在面試者回答出問題後,我總能夠瀟灑的再拋出一些問題,看著面試者露出驚異的眼神,默默一轉身,深藏功與名本文我將給大家破解深拷貝的謎題,由淺入深,環環相扣,總共涉及4種深拷貝方式,每種方式都有自己的特點和個性
  • 什麼是Java深淺拷貝?
    拷貝與Java內存結構息息相關,搞懂Java深淺拷貝是很必要的!在對象的拷貝中,很多初學者可能搞不清到底是拷貝了引用還是拷貝了對象。在拷貝中這裡就分為引用拷貝、淺拷貝、深拷貝進行講述。引用拷貝引用拷貝會生成一個新的對象引用地址,但是兩個最終指向依然是同一個對象。如何更好的理解引用拷貝呢?
  • 面試官:老是問我深淺拷貝問題,自己看文章
    面試官:老是問我深淺拷貝問題,自己看文章你如果出去面試,面試官老是會問你一些很神奇,但又不得不去了解的問題,今天這個問題就是其中之一,在工作中,其實我們經常使用對象拷貝,數組拷貝,但是往往會忽略了其中的原理,今天我們就來一探究竟。
  • 深淺拷貝知多少?
    基礎概念淺拷貝概念:只複製指向某個對象的指針,而不複製對象本身,新舊對象還是共享同一塊內存深拷貝實現方式淺拷貝的實現方式 以上了解了數據類型的存儲和拷貝的基礎概念,來具體看一下哪一些情況,賦值、拷貝是可以完全獨立存在的,哪一些情況是會改變到原有對象的內容的。請看:按照淺拷貝的基礎概念,寫了一個shallowCopy的方法,並且拷貝後同樣改變原數據中的元素,如下:
  • 阿里Java開發規約為什麼不建議使用Apache BeanUtils拷貝對象?
    是我們在實際開發中,經常會遇到將一個對象實例拷貝轉換為另一個對象實例的情況:對兩個對象的屬性進行淺(深)度複製。在具體介紹 BeanUtils 工具以前,先介紹一個有關拷貝的基礎知識。其實所有的 BeanUtils本質上就是對象拷貝工具,通常對象拷貝分為:深拷貝和淺拷貝,接下來做詳細解釋。
  • 圖解 Python 中深淺拷貝(copy)
    賦值運算l1 = [1, 2, 3, [22, 33]]l2 = l1l1.append(666)print(l1) print(l2)  圖解:注意:l2 = l1是一個指向,是賦值,和深淺小結:深copy:會在內存中開闢新空間,將原列表以及列表裡面的可變數據類型重新創建一份,不可變數據類型則沿用之前的。為什麼Python默認的拷貝方式是淺拷貝?
  • 一篇文章讀懂Python賦值與拷貝
    拷貝又分為淺拷貝和深拷貝。>>> s = [1,2,3]>>> sc = copy.copy(s) # 淺拷貝>>> sc[1, 2, 3]>>> sdc = copy.deepcopy(s)  >>> sdc[1, 2, 3]拷貝出來的對象只是值相同,實為不同的對象
  • Pandas和Numpy的視圖和拷貝
    在Numpy和Pandas中,有兩個重要概念,容易混淆,一個是淺拷貝,也稱為視圖,另外一個是深拷貝,或者就稱為拷貝。如果操作不當,Pandas會爆出SettingWithCopyWarning的異常。本文我將就視圖和拷貝問題,結合異常進行總結。
  • Java 拷貝,你能說出個 123 麼?
    作者 | sowhat1412 責編 | 張文頭圖 | CSDN 下載自視覺中國本文主要講解:對象創建方式、Java中的深拷貝和淺拷貝。本次講解的是 Java 的深拷貝和淺拷貝,其實現方式正是通過調用 Object 類的 clone() 方法來完成。