JavaScript淺層克隆與深度克隆示例詳解
1 相關知識點
- 淺克隆就是將棧記憶體中的引用複製一份,賦給一個新的變數,本質上兩個指向堆記憶體中的同一地址,內容也相同,其中一個變化另一個內容也會變化。
- 深克隆就是建立一個新的空物件,開闢一塊記憶體,然後將原物件中的資料全部複製過去,完全切斷兩個物件間的聯絡。
- 區別:淺克隆和深克隆最大的區別就是對引用值的處理了,即淺克隆之後你改我也改,深克隆之後你改我不改。(PS:原始值的處理一樣)
- 原始值(棧資料stack):Number,Boolean(false/true),String,undefined,null
- 引用值(堆資料heap):Array,Object,function ··· Date,RegExp
2 淺層克隆
在淺層克隆中,原始值的克隆沒問題,只是值的拷貝,不會出現你改我改的問題。但是引用值的克隆,就會出現你改我也改的問題,因為淺層克隆的是地址,即指向的是同一空間。
2.1 淺克隆函式
function clone(origin,target) { var target = target || {}; //容錯,即防止使用者不傳遞目標引數。若使用者傳遞了引數就用,若沒傳則拿一個空物件當目標 for (var prop in origin) { target[prop] = origin[prop]; } return target; }
2.2 運用例項
function clone(origin,target) { var target = target || {}; for (var prop in origin) { target[prop] = origin[prop]; } return target; } var obj = { name: 'abc',age: '18',sex: 'male',card: ['a','b','c'],book: { name: 'ccc',sbook: { name: 'aaa' } } }; var newobj = {}; clone(obj,newobj);
執行程式碼如下:
3 深度克隆
進行深度克隆之後,對於引用值的克隆的問題就會和原始值一樣我改你不改,因為在深度克隆中雖然是相同的東西,但是指向不同的空間。即深度克隆之後,值各自獨立,互不影響。
3.1 深克隆步驟分析
需要進行深度克隆的物件如下:
var obj = { name: 'abc',// 原始值 age: '18',// 原始值 sex: 'male',// 原始值 card: ['a',// 引用值 book: { // 引用值 name: 'ccc',// 原始值 sbook: { // 引用值 name: 'aaa'// 原始值 } } }; var obj1 = {};
(1)首先需要遍歷要克隆的物件
方法:for (var prop in origin){···}
for (var prop in origin) { ··· }
(2)依次判斷是不是原始值
方法:typeof() ,即若為原始值,就直接拷貝;若為引用值(typeof(···)返回的值是object),則進行遞迴操作。需要注意是的typeof(null) == 'object',所以得排除這一個情況。
if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') { ··· // 遞迴 } else { target[prop] = origin[prop]; }
(3)判斷是陣列還是物件
方法:toString(推薦),constructor,instanceof (後兩個會涉及到父子域的小問題,雖然遇到的可能不是很大)
var toStr = Object.prototype.toString,arrStr = "[object Array]"; if (toStr.call(origin[prop]) == arrStr) { ··· // 陣列 } else { ··· // 物件 }
(4)建立相應的陣列或物件
方法:建立一個新的同名空陣列 / 物件,並將原始物件中的 陣列或物件 當成一個新的原始物件,再次將其中的資料拷貝到目標物件的 同名空陣列 / 物件 裡面。即遞迴開始拷貝陣列或物件裡面的資料,並遞迴執行第(1)步。遞迴完成之後,再依次進行下一個資料的克隆。
var toStr = Object.prototype.toString,arrStr = "[object Array]"; if (toStr.call(origin[prop]) == arrStr) { target[prop] = []; } else { target[prop] = {}; }
newobj = { name: 'abc',card: [] // 建立一個新的同名空陣列,並把obj的card資料當成一個原始物件,再次拷貝到obj1的card裡面 // 即 遞迴開始拷貝陣列或物件裡面的資料,遞迴執行第(1)步 // 執行完陣列card拷貝之後,開始同理拷貝下一個物件book··· }
3.2 深克隆函式
function deepClone(origin,target) { var target = target || {},toStr = Object.prototype.toString,arrStr = "[object Array]"; for (var prop in origin) { if (origin.hasOwnProperty(prop)) { if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') { if (toStr.call(origin[prop]) == arrStr) { target[prop] = []; } else { target[prop] = {}; } deepClone(origin[prop],target[prop]); } else { target[prop] = origin[prop]; } } } return target; }
使用三目運算子簡化後的程式碼如下:
// 使用三目運算子簡化後 function deepClone(origin,target) { var target = (target || {}),arrStr = "[object Array]"; for (var prop in origin) { if (origin.hasOwnProperty(prop)) { if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') { target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {}; deepClone(origin[prop],target[prop]); } else { target[prop] = origin[prop]; } } } return target; }
3.3 運用例項
// 使用三目運算子簡化後 function deepClone(origin,target[prop]); } else { target[prop] = origin[prop]; } } } return target; }
執行程式碼如下:
3.4 hasOwnProperty
hasOwnProperty() 方法會返回一個布林值,指示物件自身屬性中是否具有指定的屬性(即是否有指定的鍵)。
語法:obj.hasOwnProperty(prop)
引數:要檢測的屬性的字串形式表示的名稱,或者Symbol。
返回值:用來判斷某個物件是否含有指定的屬性的布林值。
描述:所有繼承了Object的物件都會繼承到hasOwnProperty方法。這個方法可以用來檢測一個物件是否含有特定的自身屬性;和in運算子不同,該方法會忽略掉那些從原型鏈上繼承到的屬性。
用法:
a. 使用hasOwnProperty方法判斷屬性是否存在
b. 區別自身屬性與繼承屬性
c. 遍歷一個物件的所有自身屬性
d. 使用hasOwnProperty作為屬性名
具體知識點請參考 Object.prototype.hasOwnProperty()
若物件裡面編寫了原型屬性,但遍歷的時候並不想讓其顯示出來,就可以使用物件名.hasOwnProperty(屬性名) 來判斷是否是自身屬性,若是自己的則返回值為true,若不是自身原型屬性則返回值為false。例項如下:
var obj = { name: 'ABC',__proto__: { heart: 'happy' } } for (var prop in obj) { // 配套使用,起到一個過濾的作用,不把原型鏈上的資料弄出來 if (obj.hasOwnProperty(prop)) { console.log(obj[prop]);// ABC 18 male } }
個人筆記,歡迎大家交流探討!
總結
到此這篇關於JavaScript淺層克隆與深度克隆的文章就介紹到這了,更多相關JavaScript淺層克隆與深度克隆內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!