一文帶你瞭解JavaScript基礎之深拷貝和淺拷貝
拷貝(又名克隆,復制等),但是又分深拷貝和錢拷貝。
其實這個問題有時候想通瞭就很簡單,如果想不通可能會有點繞,不過其難度比閉包等好理解的多。
為什麼又這個概念的存在呢?先舉一個例子。
var person={ name:"張三", age:22 } var person1=person; console.log(person); console.log(person1);
似乎可以被拷貝下來瞭,但是如果你操作person1的屬性值,這個時候person屬性值也會改變。
person1.name="李四"; console.log(person); console.log(person1);
其實這個很容易理解,那就是上面隻是兩個對象的指針地址都是指向棧內存的相同位置,前面講解引用數據類型的時候講解過何為引用數據類型。
補充 :
對象.屬性 和對象[屬性]其實都是操作對象的屬性值,隻是兩個不同的寫法而已。
那說明這種指針賦值的方式不是拷貝,那什麼就是拷貝呢,那就是一個新的對象用用瞭一個對象的所有屬性,但彼此不受影響。
這樣的理解就明白瞭,拷貝的本質就是將一個對象的屬性循環賦值給一個新的對象而已。
那有為什麼分淺拷貝和深拷貝,說實話淺拷貝和深拷貝本質上有什麼什麼區別嗎?
其實本質沒有區別,最大的區別就是考慮的條件以及拷貝過程中的屬性類型不同而已。
老規矩先看代碼
淺拷貝
var person={ name:"張三", age:22 } var person1={}; for( key in person){ console.log(key); person1[key]=person[key]; } console.log(person); console.log(person1);
person1.name="李四"; console.log(person); console.log(person1);
可以看出沒有彼此影響,但是又會涉及到新的問題,那就是person對象的屬性都是基本數據類型,如果是引用類型呢?比如數組,對象呢?
var person={ name:"張三", age:22, son:{ firstSon:"張大毛" } } var person1={}; for( key in person){ console.log(key); person1[key]=person[key]; } console.log(person); console.log(person1);
現在修改一下person1的送屬性。
person1.son={firstSon:"李大毛"}; console.log(person); console.log(person1);
這樣看似乎也沒有彼此影響嗎?但是這個前面說過對象.屬性=這樣等於重寫賦值瞭person1.son的屬性,自然會斷開引用彼此的影響,畢竟兩個地址不一樣。但是如下修改呢?
person1.son.secondeSon="李大毛"; console.log(person); console.log(person1);
驚不驚喜,意不意外還是彼此影響瞭,這個時候就需要一種新的操作瞭那就是深拷貝,說白瞭就是將屬性值的有可能會為用引用類型。
補充:
如果person的原型上有屬性值,也會被person1取到,賦值給person1.這個前面說到過,那樣的話就會用到hasOwnProperty這個來判斷是否屬於自己的屬性值。
深拷貝
其實深拷貝和淺拷貝的區別,相信看到這裡幾乎已經明白瞭差不多瞭,就是考慮屬性值的類型而已。
// 上面補充瞭說瞭原型上的值也會被拷貝下來,為瞭方便直接用對象的原型鏈最後Object添加屬性。 Object.prototype.address="你猜"; var person={ name:"張三", age:22, son:{ firstSon:"張大毛" } } strtype=Object.prototype.toString; var person1={}; // 為瞭方便這個地方用遞歸方法 function coleFun(origin,target){ // 防止目標對象本身有屬性 target=target||{} for( key in origin){ if(origin.hasOwnProperty(key)) if(strtype.call(origin[key])=="[object Object]"){ target[key]={}; target[key]=coleFun(origin[key],target[key]) }else{ target[key]=origin[key]; } } return target; } person1=coleFun(person,person1) console.log(person); console.log(person1);
看結果沒有問題 現象修改一下屬性值試試
person1.son.secondeSon="李大毛"; console.log(person); console.log(person1);
現在看沒有問題。
所謂的深淺拷貝,說白瞭就是考慮屬性值是否會有引用類型,然後在進行拷貝而已。如果上面代碼沒有看懂的話,可能需要重溫一下引用數據和基本數據的區別,以及this指向,還有數據類型的判斷方法。這寫前面的文章都有聊過,可以翻看一下。
補充
有個朋友在評論區說如果如果用JavaScript中的JSON的方法進行拷貝數據,是深拷貝還是淺拷貝?
其實這個很容易證明的,那就是直接拷貝一個帶有引用數據類型的對象,然後判斷其是否會彼此影響即可。
首先看一下其含有的兩種方法以及作用:
方法 | 作用 |
---|---|
JSON.parse() |
用於將一個JSON 字符串轉換為 JavaScript 對象。 |
JSON.stringify() |
用於將 JavaScript 值轉換為JSON 字符串。 |
然後代碼演示:
var person={ name:"張三", age:22, son:{ firstSon:"張大毛" } } var str=JSON.stringify(person); var person1=JSON.parse(str); console.log(person); console.log(person1);
拷貝的目前看結果至少沒有問題。
現在開始測試,是否相互影響
person1.son.secondeSon="李大毛"; console.log(person); console.log(person1);
可見沒有相互影響,其是通過JOSN進行拷貝其實是JavaScript一種常見的方式,畢竟比自己寫要方便的很多。其本質就是將對象轉成一個JSON格式的字符串,而通過字符串再生成一個對象而已,所以說其也是一種深拷貝。
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- 老生常談JavaScript深拷貝與淺拷貝
- JavaScript中深拷貝與淺拷貝詳解
- 如何利用JavaScript 實現繼承
- JavaScript深拷貝的幾種實現方法實例
- JavaScript 賦值,淺復制和深復制的區別