關於本篇文章的起源是一位大佬在面試的時候,詢問應聘者關於淺拷貝的知識後,在應聘者的回答中,筆者發現有好一部分人對淺拷貝都是錯誤的,故有了此篇內容。
1. 還原現場
大佬:「如何複製一個對象?」
她: 「複製對象有深拷貝和淺拷貝...」
大佬:」說一下這兩者之間的區別「
她: 」我給你寫一段淺拷貝的代碼「
var a = { x: 1 };var b = a;大佬:」回去等通知吧 ~.~「
2. 一探究竟
剛開始看到上面應聘者的例子的時候,其實我也認為應聘者寫的是對的,因為在我的記憶裡,對象的淺拷貝就是兩個變量存儲的值是相同的堆地址,而上面應聘者寫的 a 和 b 符合這個條件,但是大佬提出異議後,便立刻去google了一把,發現Javascript的淺拷貝居然沒有官方定義(有人找到的話麻煩評論區貼一個地址,非常感謝)。
於是我去MDN(https://developer.mozilla.org)上查了一下shallow copy的關鍵字,也沒有關於shallow copy的準確定義,這也難怪部分人對於淺拷貝會有不同的認識。
第一種定義:一個新的對象直接拷貝已存在的對象的引用,即淺拷貝。
第二種定義:一個新的對象直接拷貝已存在的對象的對象屬性的引用,即淺拷貝。
第一種和第二種的差異即是,對象本身引入與對象的對象屬性的引入,因為我沒有找到標準的關於淺拷貝的官方定義,所以對於這兩種方式便開始思考想辦法去驗證。
在前面MDN中搜索shallow copy時,雖然沒有找到它的定義,但是找了一個一些其他的內容。
Array.prototype.slice()屬於淺拷貝,那我們來驗證一下Array.prototype.slice()返回的新數組對象和老的數組對象之間究竟符合那種關係。
var a = [ 1, 3, 5, { x: 1 } ];var b = Array.prototype.slice.call(a);b[0] = 2;console.log(a); console.log(b);很明顯,屬於淺拷貝的a和b並不是第一種定義所描述的,如果a和b是相同的引用對象,當b[0]改變時a[0]應當是跟著改變。
var a = [ 1, 3, 5, { x: 1 } ];var b = Array.prototype.slice.call(a);b[3].x = 2;console.log(a); console.log(b);通過上面的代碼既可以看出,淺拷貝的正確定義是第二種,只拷貝已存在對象的對象屬性的引用,其餘非對象屬性是佔用新的內存空間,並非與原對象共享。
3. 歸納總結
通過上面的內容,理清楚了淺拷貝的定義,而隨之引出的」深拷貝「又是怎樣的?和」淺拷貝「有什麼關係?在下面總結一番。
淺拷貝:新的對象複製已有對象中非對象屬性的值和對象屬性的引用。
簡單實現一個淺拷貝函數:
var a = [ 1, 3, 5, { x: 1 } ];function copy(a){ let b = Array.isArray(a) ? [] : {}; for (let i in a) { b[i] = a[i]; } return b;}像常用的數組方法slice和對象方法Object.assign都屬於淺拷貝。
深拷貝:遍歷一個對象中所有的屬性的值及對象屬性中的屬性值,不論是嵌套了幾層,要完成所有對象屬性的遞歸後,賦值給一個新的對象。
var a = { x: 1, y: { x: 1 } };function copy(data) {var b={}; if (typeof data === 'number') { return data } for (var i in data) { if (data.hasOwnProperty(i)) { b[i] = copy(data[i]); } } return b;}也可以使用快捷的深拷貝方式,完成對象複製,但是這種方式要求所要複製的對象的屬性值非函數。
var b = JSON.parse(JSON.stringify(a));如上內容均為自己總結,難免會有錯誤或者認識偏差,如有問題,希望大家留言指正,以免誤人。
如果覺得本文有幫助,歡迎多多轉發點讚,推薦給更多的小夥伴喲
更多前端系統學習視頻教程,點擊這裡獲取哦:https://study.miaov.com/study
好消息~好消息~即日起新註冊的用戶,可享受新人「VIP視頻」禮包哦,註冊後記得添加微信客服:miaov_xy 來獲取哦