Javascript深淺拷貝的原理
在實際開發當中,我們經常會遇到要對物件進行深拷貝的情況。而且深拷貝這個問題在面試過程中也經常會遇到,下面就對本人在學習過程中的收穫,做以簡單的總結。
什麼是淺拷貝,什麼是深拷貝?
什麼是淺拷貝
關於淺拷貝的概念,我在網上看到一種說法,直接上程式碼。
12 | var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};var person1 = person; //他們認為這是淺拷貝 |
但是我個人認為,上面這個根本不涉及拷貝,只是一個簡單的引用賦值。以我的理解,淺拷貝應該是不考慮物件的引用型別的屬性,只對當前物件的所有成員進行拷貝,程式碼如下:
12345678910 | function copy(obj){ var objCopy = {}; for(var key in obj){ objCopy[key] = obj[key]; } return objCopy;}var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};var personCopy = copy(person); |
上面這段程式碼中,person
物件擁有兩個基本型別的屬性name
和age
,一個引用型別的屬性car
,當使用如上方法進行拷貝的時候,name
age
屬性會被正常的拷貝,但是car
屬性,只會進行引用的拷貝,這樣會導致拷貝出來的物件personCopy
和person
會共用一個car
物件。這樣就是所謂的淺拷貝。
什麼是深拷貝
深拷貝的就是在拷貝的時候,需要將當前要拷貝的物件內的所有引用型別的屬性進行完整的拷貝,也就是說拷貝出來的物件和原物件之間沒有任何資料是共享的,所有的東西都是自己獨佔的一份。
如何實現深拷貝
實現深拷貝需要考慮的問題
實現深拷貝需要考慮如下幾個因素:
- 傳入的物件是使用物件字面量
{}
建立的物件還是由建構函式生成的物件 - 如果物件是由建構函式創建出來的,那麼是否要拷貝原型鏈上的屬性
- 如果要拷貝原型鏈上的屬性,那麼如果原型鏈上存在多個同名的屬性,保留哪個
- 處理迴圈引用的問題
第三方庫實現深拷貝
我們可以通過$.extend()</code>方法來完成深複製。值得慶幸的是,我們在<code>jQuery</code>中可以通過新增一個引數來實現遞迴<code>extend</code>。呼叫<code>$.extend(true, {}, ...)
就可以實現深複製,參考下面的例子:
1234567891011 | var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ]};var y = $.extend({}, x), <span class="comment">//shallow copy</span></div><div class="line"> z = $.extend(true, {}, x); //deep copyy.b.f === x.b.f // truez.b.f === x.b.f // false |
但是jQuery的這個$.extend()</code>方法,有弊端,什麼弊端呢?我們看下面的例子:<br></p><figure class="highlight js"><table><tbody><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code" style="width: 647.5px;"><pre style="width: 647.5px;"><div class="line"><span class="keyword">var</span> objA = {};</div><div class="line"><span class="keyword">var</span> objB = {};</div><div class="line"></div><div class="line">objA.b = objB;</div><div class="line">objB.a = objA;</div><div class="line"></div><div class="line">$.extend(true,{},a);